mirror of
https://github.com/nasa/openmct.git
synced 2024-12-24 23:36:41 +00:00
Provide visibility based rendering as part of the view api (#7241) * first draft * in preview mode, just show it * fix unit tests
This commit is contained in:
parent
15ee8303e4
commit
f0dcf2ba21
@ -34,7 +34,7 @@ export default class LADTableView {
|
||||
this._destroy = null;
|
||||
}
|
||||
|
||||
show(element) {
|
||||
show(element, isEditing, { renderWhenVisible }) {
|
||||
let ladTableConfiguration = new LADTableConfiguration(this.domainObject, this.openmct);
|
||||
|
||||
const { vNode, destroy } = mount(
|
||||
@ -46,7 +46,8 @@ export default class LADTableView {
|
||||
provide: {
|
||||
openmct: this.openmct,
|
||||
currentView: this,
|
||||
ladTableConfiguration
|
||||
ladTableConfiguration,
|
||||
renderWhenVisible
|
||||
},
|
||||
data: () => {
|
||||
return {
|
||||
|
@ -54,12 +54,11 @@ const BLANK_VALUE = '---';
|
||||
import identifierToString from '/src/tools/url';
|
||||
import PreviewAction from '@/ui/preview/PreviewAction.js';
|
||||
|
||||
import NicelyCalled from '../../../api/nice/NicelyCalled';
|
||||
import tooltipHelpers from '../../../api/tooltips/tooltipMixins';
|
||||
|
||||
export default {
|
||||
mixins: [tooltipHelpers],
|
||||
inject: ['openmct', 'currentView'],
|
||||
inject: ['openmct', 'currentView', 'renderWhenVisible'],
|
||||
props: {
|
||||
domainObject: {
|
||||
type: Object,
|
||||
@ -190,7 +189,6 @@ export default {
|
||||
}
|
||||
},
|
||||
async mounted() {
|
||||
this.nicelyCalled = new NicelyCalled(this.$refs.tableRow);
|
||||
this.metadata = this.openmct.telemetry.getMetadata(this.domainObject);
|
||||
this.formats = this.openmct.telemetry.getFormatMap(this.metadata);
|
||||
this.keyString = this.openmct.objects.makeKeyString(this.domainObject.identifier);
|
||||
@ -239,12 +237,11 @@ export default {
|
||||
this.previewAction.off('isVisible', this.togglePreviewState);
|
||||
|
||||
this.telemetryCollection.destroy();
|
||||
this.nicelyCalled.destroy();
|
||||
},
|
||||
methods: {
|
||||
updateView() {
|
||||
if (!this.updatingView) {
|
||||
this.updatingView = this.nicelyCalled.execute(() => {
|
||||
this.updatingView = this.renderWhenVisible(() => {
|
||||
this.timestamp = this.getParsedTimestamp(this.latestDatum);
|
||||
this.datum = this.latestDatum;
|
||||
this.updatingView = false;
|
||||
|
@ -24,6 +24,7 @@ import {
|
||||
getLatestTelemetry,
|
||||
getMockObjects,
|
||||
getMockTelemetry,
|
||||
renderWhenVisible,
|
||||
resetApplicationState,
|
||||
spyOnBuiltins
|
||||
} from 'utils/testing';
|
||||
@ -225,7 +226,7 @@ describe('The LAD Table', () => {
|
||||
(viewProvider) => viewProvider.key === ladTableKey
|
||||
);
|
||||
ladTableView = ladTableViewProvider.view(mockObj.ladTable, [mockObj.ladTable]);
|
||||
ladTableView.show(child, true);
|
||||
ladTableView.show(child, true, { renderWhenVisible });
|
||||
|
||||
await Promise.all([
|
||||
telemetryRequestPromise,
|
||||
|
@ -73,14 +73,18 @@ import {
|
||||
} from '@/plugins/notebook/utils/notebook-storage.js';
|
||||
import stalenessMixin from '@/ui/mixins/staleness-mixin';
|
||||
|
||||
import NicelyCalled from '../../../api/nice/NicelyCalled';
|
||||
import tooltipHelpers from '../../../api/tooltips/tooltipMixins';
|
||||
import conditionalStylesMixin from '../mixins/objectStyles-mixin';
|
||||
import LayoutFrame from './LayoutFrame.vue';
|
||||
|
||||
const DEFAULT_TELEMETRY_DIMENSIONS = [10, 5];
|
||||
const DEFAULT_POSITION = [1, 1];
|
||||
const CONTEXT_MENU_ACTIONS = ['copyToClipboard', 'copyToNotebook', 'viewHistoricalData'];
|
||||
const CONTEXT_MENU_ACTIONS = [
|
||||
'copyToClipboard',
|
||||
'copyToNotebook',
|
||||
'viewHistoricalData',
|
||||
'renderWhenVisible'
|
||||
];
|
||||
|
||||
export default {
|
||||
makeDefinition(openmct, gridSize, domainObject, position) {
|
||||
@ -106,7 +110,7 @@ export default {
|
||||
LayoutFrame
|
||||
},
|
||||
mixins: [conditionalStylesMixin, stalenessMixin, tooltipHelpers],
|
||||
inject: ['openmct', 'objectPath', 'currentView'],
|
||||
inject: ['openmct', 'objectPath', 'currentView', 'renderWhenVisible'],
|
||||
props: {
|
||||
item: {
|
||||
type: Object,
|
||||
@ -274,7 +278,6 @@ export default {
|
||||
}
|
||||
this.setObject(foundObject);
|
||||
await this.$nextTick();
|
||||
this.nicelyCalled = new NicelyCalled(this.$refs.telemetryViewWrapper);
|
||||
},
|
||||
formattedValueForCopy() {
|
||||
const timeFormatterKey = this.openmct.time.timeSystem().key;
|
||||
@ -291,7 +294,7 @@ export default {
|
||||
},
|
||||
updateView() {
|
||||
if (!this.updatingView) {
|
||||
this.updatingView = this.nicelyCalled.execute(() => {
|
||||
this.updatingView = this.renderWhenVisible(() => {
|
||||
this.datum = this.latestDatum;
|
||||
this.updatingView = false;
|
||||
});
|
||||
|
@ -39,7 +39,7 @@ class DisplayLayoutView {
|
||||
this.component = null;
|
||||
}
|
||||
|
||||
show(container, isEditing) {
|
||||
show(container, isEditing, { renderWhenVisible }) {
|
||||
const { vNode, destroy } = mount(
|
||||
{
|
||||
el: container,
|
||||
@ -50,7 +50,8 @@ class DisplayLayoutView {
|
||||
openmct: this.openmct,
|
||||
objectPath: this.objectPath,
|
||||
options: this.options,
|
||||
currentView: this
|
||||
currentView: this,
|
||||
renderWhenVisible
|
||||
},
|
||||
data: () => {
|
||||
return {
|
||||
|
@ -20,7 +20,7 @@
|
||||
* at runtime from the About dialog for additional information.
|
||||
*****************************************************************************/
|
||||
|
||||
import { createOpenMct, resetApplicationState } from 'utils/testing';
|
||||
import { createOpenMct, renderWhenVisible, resetApplicationState } from 'utils/testing';
|
||||
import { nextTick } from 'vue';
|
||||
|
||||
import DisplayLayoutPlugin from './plugin';
|
||||
@ -114,7 +114,7 @@ describe('the plugin', function () {
|
||||
let error;
|
||||
|
||||
try {
|
||||
view.show(child, false);
|
||||
view.show(child, false, { renderWhenVisible });
|
||||
} catch (e) {
|
||||
error = e;
|
||||
}
|
||||
@ -161,7 +161,7 @@ describe('the plugin', function () {
|
||||
(viewProvider) => viewProvider.key === 'layout.view'
|
||||
);
|
||||
const view = displayLayoutViewProvider.view(displayLayoutItem, displayLayoutItem);
|
||||
view.show(child, false);
|
||||
view.show(child, false, { renderWhenVisible });
|
||||
|
||||
nextTick(done);
|
||||
});
|
||||
|
@ -20,7 +20,7 @@
|
||||
* at runtime from the About dialog for additional information.
|
||||
*****************************************************************************/
|
||||
import { debounce } from 'lodash';
|
||||
import { createOpenMct, resetApplicationState } from 'utils/testing';
|
||||
import { createOpenMct, renderWhenVisible, resetApplicationState } from 'utils/testing';
|
||||
import { nextTick } from 'vue';
|
||||
|
||||
let gaugeDomainObject = {
|
||||
@ -172,7 +172,7 @@ describe('Gauge plugin', () => {
|
||||
return openmct.objects.getMutable(gaugeViewObject.identifier).then((mutableObject) => {
|
||||
mutablegaugeObject = mutableObject;
|
||||
gaugeView = gaugeViewProvider.view(mutablegaugeObject, [mutablegaugeObject]);
|
||||
gaugeView.show(child);
|
||||
gaugeView.show(child, false, { renderWhenVisible });
|
||||
|
||||
return nextTick();
|
||||
});
|
||||
@ -314,7 +314,7 @@ describe('Gauge plugin', () => {
|
||||
return openmct.objects.getMutable(gaugeViewObject.identifier).then((mutableObject) => {
|
||||
mutablegaugeObject = mutableObject;
|
||||
gaugeView = gaugeViewProvider.view(mutablegaugeObject, [mutablegaugeObject]);
|
||||
gaugeView.show(child);
|
||||
gaugeView.show(child, false, { renderWhenVisible });
|
||||
|
||||
return nextTick();
|
||||
});
|
||||
@ -456,7 +456,7 @@ describe('Gauge plugin', () => {
|
||||
return openmct.objects.getMutable(gaugeViewObject.identifier).then((mutableObject) => {
|
||||
mutablegaugeObject = mutableObject;
|
||||
gaugeView = gaugeViewProvider.view(mutablegaugeObject, [mutablegaugeObject]);
|
||||
gaugeView.show(child);
|
||||
gaugeView.show(child, false, { renderWhenVisible });
|
||||
|
||||
return nextTick();
|
||||
});
|
||||
@ -560,7 +560,7 @@ describe('Gauge plugin', () => {
|
||||
mutablegaugeObject = mutableObject;
|
||||
|
||||
gaugeView = gaugeViewProvider.view(mutablegaugeObject, [mutablegaugeObject]);
|
||||
gaugeView.show(child);
|
||||
gaugeView.show(child, false, { renderWhenVisible });
|
||||
|
||||
return nextTick();
|
||||
});
|
||||
@ -643,7 +643,7 @@ describe('Gauge plugin', () => {
|
||||
mutablegaugeObject = mutableObject;
|
||||
|
||||
gaugeView = gaugeViewProvider.view(mutablegaugeObject, [mutablegaugeObject]);
|
||||
gaugeView.show(child);
|
||||
gaugeView.show(child, false, { renderWhenVisible });
|
||||
|
||||
return nextTick();
|
||||
});
|
||||
@ -771,7 +771,7 @@ describe('Gauge plugin', () => {
|
||||
return openmct.objects.getMutable(gaugeViewObject.identifier).then((mutableObject) => {
|
||||
mutablegaugeObject = mutableObject;
|
||||
gaugeView = gaugeViewProvider.view(mutablegaugeObject, [mutablegaugeObject]);
|
||||
gaugeView.show(child);
|
||||
gaugeView.show(child, false, { renderWhenVisible });
|
||||
|
||||
return nextTick();
|
||||
});
|
||||
|
@ -41,7 +41,7 @@ export default function GaugeViewProvider(openmct) {
|
||||
let _destroy = null;
|
||||
|
||||
return {
|
||||
show: function (element) {
|
||||
show: function (element, isEditing, { renderWhenVisible }) {
|
||||
const { destroy } = mount(
|
||||
{
|
||||
el: element,
|
||||
@ -51,7 +51,8 @@ export default function GaugeViewProvider(openmct) {
|
||||
provide: {
|
||||
openmct,
|
||||
domainObject,
|
||||
composition: openmct.composition.get(domainObject)
|
||||
composition: openmct.composition.get(domainObject),
|
||||
renderWhenVisible
|
||||
},
|
||||
template: '<gauge-component></gauge-component>'
|
||||
},
|
||||
|
@ -336,7 +336,6 @@
|
||||
<script>
|
||||
import stalenessMixin from '@/ui/mixins/staleness-mixin';
|
||||
|
||||
import NicelyCalled from '../../../api/nice/NicelyCalled';
|
||||
import tooltipHelpers from '../../../api/tooltips/tooltipMixins';
|
||||
import { DIAL_VALUE_DEG_OFFSET, getLimitDegree } from '../gauge-limit-util';
|
||||
|
||||
@ -345,7 +344,7 @@ const DEFAULT_CURRENT_VALUE = '--';
|
||||
|
||||
export default {
|
||||
mixins: [stalenessMixin, tooltipHelpers],
|
||||
inject: ['openmct', 'domainObject', 'composition'],
|
||||
inject: ['openmct', 'domainObject', 'composition', 'renderWhenVisible'],
|
||||
data() {
|
||||
let gaugeController = this.domainObject.configuration.gaugeController;
|
||||
|
||||
@ -539,7 +538,6 @@ export default {
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.nicelyCalled = new NicelyCalled(this.$refs.gaugeWrapper);
|
||||
this.composition.on('add', this.addedToComposition);
|
||||
this.composition.on('remove', this.removeTelemetryObject);
|
||||
|
||||
@ -563,8 +561,6 @@ export default {
|
||||
|
||||
this.openmct.time.off('bounds', this.refreshData);
|
||||
this.openmct.time.off('timeSystem', this.setTimeSystem);
|
||||
|
||||
this.nicelyCalled.destroy();
|
||||
},
|
||||
methods: {
|
||||
getLimitDegree: getLimitDegree,
|
||||
@ -737,7 +733,7 @@ export default {
|
||||
return;
|
||||
}
|
||||
|
||||
this.isRendering = this.nicelyCalled.execute(() => {
|
||||
this.isRendering = this.renderWhenVisible(() => {
|
||||
this.isRendering = false;
|
||||
|
||||
this.curVal = this.round(this.formats[this.valueKey].format(this.datum), this.precision);
|
||||
|
@ -633,13 +633,22 @@ describe('The Imagery View Layouts', () => {
|
||||
imageWrapper[2].dispatchEvent(mouseDownEvent);
|
||||
await nextTick();
|
||||
const timestamp = imageWrapper[2].id.replace('wrapper-', '');
|
||||
expect(componentView.previewAction.invoke).toHaveBeenCalledWith(
|
||||
[componentView.objectPath[0]],
|
||||
{
|
||||
timestamp: Number(timestamp),
|
||||
objectPath: componentView.objectPath
|
||||
}
|
||||
);
|
||||
const mockInvoke = componentView.previewAction.invoke;
|
||||
// Make sure the function was called
|
||||
expect(mockInvoke).toHaveBeenCalled();
|
||||
|
||||
// Get the arguments of the first call
|
||||
const firstArg = mockInvoke.calls.mostRecent().args[0];
|
||||
const secondArg = mockInvoke.calls.mostRecent().args[1];
|
||||
|
||||
// Compare the first argument
|
||||
expect(firstArg).toEqual([componentView.objectPath[0]]);
|
||||
|
||||
// Compare the "timestamp" property of the second argument
|
||||
expect(secondArg.timestamp).toEqual(Number(timestamp));
|
||||
|
||||
// Compare the "objectPath" property of the second argument
|
||||
expect(secondArg.objectPath).toEqual(componentView.objectPath);
|
||||
});
|
||||
|
||||
it('should remove images when clock advances', async () => {
|
||||
|
@ -198,7 +198,7 @@ export default {
|
||||
MctTicks,
|
||||
MctChart
|
||||
},
|
||||
inject: ['openmct', 'domainObject', 'path'],
|
||||
inject: ['openmct', 'domainObject', 'path', 'renderWhenVisible'],
|
||||
props: {
|
||||
options: {
|
||||
type: Object,
|
||||
|
@ -65,7 +65,7 @@ export default function PlotViewProvider(openmct) {
|
||||
let component = null;
|
||||
|
||||
return {
|
||||
show: function (element) {
|
||||
show: function (element, isEditing, { renderWhenVisible }) {
|
||||
let isCompact = isCompactView(objectPath);
|
||||
const { vNode, destroy } = mount(
|
||||
{
|
||||
@ -76,7 +76,8 @@ export default function PlotViewProvider(openmct) {
|
||||
provide: {
|
||||
openmct,
|
||||
domainObject,
|
||||
path: objectPath
|
||||
path: objectPath,
|
||||
renderWhenVisible
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
|
@ -45,7 +45,6 @@
|
||||
import mount from 'utils/mount';
|
||||
import { toRaw } from 'vue';
|
||||
|
||||
import NicelyCalled from '../../../api/nice/NicelyCalled';
|
||||
import configStore from '../configuration/ConfigStore';
|
||||
import PlotConfigurationModel from '../configuration/PlotConfigurationModel';
|
||||
import { DrawLoader } from '../draw/DrawLoader';
|
||||
@ -100,7 +99,7 @@ const HANDLED_ATTRIBUTES = {
|
||||
|
||||
export default {
|
||||
components: { LimitLine, LimitLabel },
|
||||
inject: ['openmct', 'domainObject', 'path'],
|
||||
inject: ['openmct', 'domainObject', 'path', 'renderWhenVisible'],
|
||||
props: {
|
||||
rectangles: {
|
||||
type: Array,
|
||||
@ -199,7 +198,6 @@ export default {
|
||||
},
|
||||
mounted() {
|
||||
eventHelpers.extend(this);
|
||||
this.nicelyCalled = new NicelyCalled(this.$refs.chart);
|
||||
this.seriesModels = [];
|
||||
this.config = this.getConfig();
|
||||
this.isDestroyed = false;
|
||||
@ -258,7 +256,6 @@ export default {
|
||||
},
|
||||
beforeUnmount() {
|
||||
this.destroy();
|
||||
this.nicelyCalled.destroy();
|
||||
},
|
||||
methods: {
|
||||
getConfig() {
|
||||
@ -650,7 +647,7 @@ export default {
|
||||
},
|
||||
scheduleDraw() {
|
||||
if (!this.drawScheduled) {
|
||||
const called = this.nicelyCalled.execute(this.draw);
|
||||
const called = this.renderWhenVisible(this.draw);
|
||||
this.drawScheduled = called;
|
||||
}
|
||||
},
|
||||
|
@ -47,7 +47,7 @@ export default function OverlayPlotViewProvider(openmct) {
|
||||
let component = null;
|
||||
|
||||
return {
|
||||
show: function (element) {
|
||||
show: function (element, isEditing, { renderWhenVisible }) {
|
||||
let isCompact = isCompactView(objectPath);
|
||||
const { vNode, destroy } = mount(
|
||||
{
|
||||
@ -58,7 +58,8 @@ export default function OverlayPlotViewProvider(openmct) {
|
||||
provide: {
|
||||
openmct,
|
||||
domainObject,
|
||||
path: objectPath
|
||||
path: objectPath,
|
||||
renderWhenVisible
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
|
@ -25,6 +25,7 @@ import mount from 'utils/mount';
|
||||
import {
|
||||
createMouseEvent,
|
||||
createOpenMct,
|
||||
renderWhenVisible,
|
||||
resetApplicationState,
|
||||
spyOnBuiltins
|
||||
} from 'utils/testing';
|
||||
@ -314,7 +315,8 @@ describe('the plugin', function () {
|
||||
openmct,
|
||||
domainObject: overlayPlotObject,
|
||||
composition,
|
||||
path: [overlayPlotObject]
|
||||
path: [overlayPlotObject],
|
||||
renderWhenVisible
|
||||
},
|
||||
template: '<plot ref="plotComponent"></plot>'
|
||||
},
|
||||
@ -505,7 +507,8 @@ describe('the plugin', function () {
|
||||
openmct: openmct,
|
||||
domainObject: overlayPlotObject,
|
||||
composition,
|
||||
path: [overlayPlotObject]
|
||||
path: [overlayPlotObject],
|
||||
renderWhenVisible
|
||||
},
|
||||
template: '<plot ref="plotComponent"></plot>'
|
||||
},
|
||||
|
@ -25,6 +25,7 @@ import mount from 'utils/mount';
|
||||
import {
|
||||
createMouseEvent,
|
||||
createOpenMct,
|
||||
renderWhenVisible,
|
||||
resetApplicationState,
|
||||
spyOnBuiltins
|
||||
} from 'utils/testing';
|
||||
@ -372,7 +373,7 @@ describe('the plugin', function () {
|
||||
applicableViews = openmct.objectViews.get(testTelemetryObject, mockObjectPath);
|
||||
plotViewProvider = applicableViews.find((viewProvider) => viewProvider.key === 'plot-single');
|
||||
plotView = plotViewProvider.view(testTelemetryObject, []);
|
||||
plotView.show(child, true);
|
||||
plotView.show(child, true, { renderWhenVisible });
|
||||
|
||||
return nextTick();
|
||||
});
|
||||
@ -654,7 +655,7 @@ describe('the plugin', function () {
|
||||
plotViewProvider = applicableViews.find((viewProvider) => viewProvider.key === 'plot-single');
|
||||
plotView = plotViewProvider.view(testTelemetryObject, []);
|
||||
|
||||
plotView.show(child, true);
|
||||
plotView.show(child, true, { renderWhenVisible });
|
||||
|
||||
resizePromise = new Promise((resolve) => {
|
||||
resizePromiseResolve = resolve;
|
||||
@ -811,7 +812,8 @@ describe('the plugin', function () {
|
||||
provide: {
|
||||
openmct: openmct,
|
||||
domainObject: selection[0][0].context.item,
|
||||
path: [selection[0][0].context.item, selection[0][1].context.item]
|
||||
path: [selection[0][0].context.item, selection[0][1].context.item],
|
||||
renderWhenVisible
|
||||
},
|
||||
template: '<plot-options ref="root"/>'
|
||||
},
|
||||
|
@ -76,7 +76,7 @@ export default {
|
||||
StackedPlotItem,
|
||||
PlotLegend
|
||||
},
|
||||
inject: ['openmct', 'domainObject', 'path'],
|
||||
inject: ['openmct', 'domainObject', 'path', 'renderWhenVisible'],
|
||||
props: {
|
||||
options: {
|
||||
type: Object,
|
||||
|
@ -34,7 +34,7 @@ import conditionalStylesMixin from './mixins/objectStyles-mixin';
|
||||
|
||||
export default {
|
||||
mixins: [conditionalStylesMixin, stalenessMixin],
|
||||
inject: ['openmct', 'domainObject', 'path'],
|
||||
inject: ['openmct', 'domainObject', 'path', 'renderWhenVisible'],
|
||||
props: {
|
||||
childObject: {
|
||||
type: Object,
|
||||
@ -217,7 +217,8 @@ export default {
|
||||
provide: {
|
||||
openmct,
|
||||
domainObject: object,
|
||||
path
|
||||
path,
|
||||
renderWhenVisible: this.renderWhenVisible
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
|
@ -48,7 +48,7 @@ export default function StackedPlotViewProvider(openmct) {
|
||||
let component = null;
|
||||
|
||||
return {
|
||||
show: function (element) {
|
||||
show: function (element, isEditing, { renderWhenVisible }) {
|
||||
let isCompact = isCompactView(objectPath);
|
||||
|
||||
const { vNode, destroy } = mount(
|
||||
@ -60,7 +60,8 @@ export default function StackedPlotViewProvider(openmct) {
|
||||
provide: {
|
||||
openmct,
|
||||
domainObject,
|
||||
path: objectPath
|
||||
path: objectPath,
|
||||
renderWhenVisible
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
|
@ -25,6 +25,7 @@ import mount from 'utils/mount';
|
||||
import {
|
||||
createMouseEvent,
|
||||
createOpenMct,
|
||||
renderWhenVisible,
|
||||
resetApplicationState,
|
||||
spyOnBuiltins
|
||||
} from 'utils/testing';
|
||||
@ -329,7 +330,8 @@ describe('the plugin', function () {
|
||||
provide: {
|
||||
openmct,
|
||||
domainObject: stackedPlotObject,
|
||||
path: [stackedPlotObject]
|
||||
path: [stackedPlotObject],
|
||||
renderWhenVisible
|
||||
},
|
||||
template: '<stacked-plot ref="stackedPlotRef"></stacked-plot>'
|
||||
},
|
||||
@ -619,7 +621,8 @@ describe('the plugin', function () {
|
||||
provide: {
|
||||
openmct: openmct,
|
||||
domainObject: selection[0][0].context.item,
|
||||
path: [selection[0][0].context.item]
|
||||
path: [selection[0][0].context.item],
|
||||
renderWhenVisible
|
||||
},
|
||||
template: '<plot-options/>'
|
||||
},
|
||||
@ -774,7 +777,8 @@ describe('the plugin', function () {
|
||||
provide: {
|
||||
openmct: openmct,
|
||||
domainObject: selection[0][0].context.item,
|
||||
path: [selection[0][0].context.item, selection[0][1].context.item]
|
||||
path: [selection[0][0].context.item, selection[0][1].context.item],
|
||||
renderWhenVisible
|
||||
},
|
||||
template: '<plot-options />'
|
||||
},
|
||||
|
@ -65,7 +65,7 @@ export default class TelemetryTableView {
|
||||
}
|
||||
}
|
||||
|
||||
show(element, editMode) {
|
||||
show(element, editMode, { renderWhenVisible }) {
|
||||
const { vNode, destroy } = mount(
|
||||
{
|
||||
el: element,
|
||||
@ -76,7 +76,8 @@ export default class TelemetryTableView {
|
||||
openmct: this.openmct,
|
||||
objectPath: this.objectPath,
|
||||
table: this.table,
|
||||
currentView: this
|
||||
currentView: this,
|
||||
renderWhenVisible
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
|
@ -280,7 +280,6 @@ import { toRaw } from 'vue';
|
||||
|
||||
import stalenessMixin from '@/ui/mixins/staleness-mixin';
|
||||
|
||||
import NicelyCalled from '../../../api/nice/NicelyCalled';
|
||||
import CSVExporter from '../../../exporters/CSVExporter.js';
|
||||
import ProgressBar from '../../../ui/components/ProgressBar.vue';
|
||||
import Search from '../../../ui/components/SearchComponent.vue';
|
||||
@ -306,7 +305,7 @@ export default {
|
||||
ProgressBar
|
||||
},
|
||||
mixins: [stalenessMixin],
|
||||
inject: ['openmct', 'objectPath', 'table', 'currentView'],
|
||||
inject: ['openmct', 'objectPath', 'table', 'currentView', 'renderWhenVisible'],
|
||||
props: {
|
||||
isEditing: {
|
||||
type: Boolean,
|
||||
@ -481,7 +480,6 @@ export default {
|
||||
this.filterChanged = _.debounce(this.filterChanged, 500);
|
||||
},
|
||||
mounted() {
|
||||
this.nicelyCalled = new NicelyCalled(this.$refs.root);
|
||||
this.csvExporter = new CSVExporter();
|
||||
this.rowsAdded = _.throttle(this.rowsAdded, 200);
|
||||
this.rowsRemoved = _.throttle(this.rowsRemoved, 200);
|
||||
@ -547,13 +545,11 @@ export default {
|
||||
this.table.configuration.destroy();
|
||||
|
||||
this.table.destroy();
|
||||
|
||||
this.nicelyCalled.destroy();
|
||||
},
|
||||
methods: {
|
||||
updateVisibleRows() {
|
||||
if (!this.updatingView) {
|
||||
this.updatingView = this.nicelyCalled.execute(() => {
|
||||
this.updatingView = this.renderWhenVisible(() => {
|
||||
let start = 0;
|
||||
let end = VISIBLE_ROW_COUNT;
|
||||
let tableRows = this.table.tableRows.getRows();
|
||||
@ -829,7 +825,7 @@ export default {
|
||||
let scrollTop = this.scrollable.scrollTop;
|
||||
|
||||
this.resizePollHandle = setInterval(() => {
|
||||
this.nicelyCalled.execute(() => {
|
||||
this.renderWhenVisible(() => {
|
||||
if ((el.clientWidth !== width || el.clientHeight !== height) && this.isAutosizeEnabled) {
|
||||
this.calculateTableSize();
|
||||
// On some resize events scrollTop is reset to 0. Possibly due to a transition we're using?
|
||||
|
@ -22,6 +22,7 @@
|
||||
import {
|
||||
createMouseEvent,
|
||||
createOpenMct,
|
||||
renderWhenVisible,
|
||||
resetApplicationState,
|
||||
spyOnBuiltins
|
||||
} from 'utils/testing';
|
||||
@ -236,7 +237,7 @@ describe('the plugin', () => {
|
||||
applicableViews = openmct.objectViews.get(testTelemetryObject, []);
|
||||
tableViewProvider = applicableViews.find((viewProvider) => viewProvider.key === 'table');
|
||||
tableView = tableViewProvider.view(testTelemetryObject, [testTelemetryObject]);
|
||||
tableView.show(child, true);
|
||||
tableView.show(child, true, { renderWhenVisible });
|
||||
|
||||
tableInstance = tableView.getTable();
|
||||
|
||||
|
@ -33,6 +33,8 @@ import StyleRuleManager from '@/plugins/condition/StyleRuleManager';
|
||||
import { STYLE_CONSTANTS } from '@/plugins/condition/utils/constants';
|
||||
import stalenessMixin from '@/ui/mixins/staleness-mixin';
|
||||
|
||||
import VisibilityObserver from '../../utils/visibility/VisibilityObserver';
|
||||
|
||||
export default {
|
||||
mixins: [stalenessMixin],
|
||||
inject: ['openmct'],
|
||||
@ -113,6 +115,9 @@ export default {
|
||||
this.actionCollection.destroy();
|
||||
delete this.actionCollection;
|
||||
}
|
||||
if (this.visibilityObserver) {
|
||||
this.visibilityObserver.destroy();
|
||||
}
|
||||
this.$refs.objectViewWrapper.removeEventListener('dragover', this.onDragOver, {
|
||||
capture: true
|
||||
});
|
||||
@ -125,6 +130,7 @@ export default {
|
||||
this.debounceUpdateView = _.debounce(this.updateView, 10);
|
||||
},
|
||||
mounted() {
|
||||
this.visibilityObserver = new VisibilityObserver(this.$refs.objectViewWrapper);
|
||||
this.updateView();
|
||||
this.$refs.objectViewWrapper.addEventListener('dragover', this.onDragOver, {
|
||||
capture: true
|
||||
@ -290,7 +296,9 @@ export default {
|
||||
}
|
||||
}
|
||||
|
||||
this.currentView.show(this.viewContainer, this.openmct.editor.isEditing());
|
||||
this.currentView.show(this.viewContainer, this.openmct.editor.isEditing(), {
|
||||
renderWhenVisible: this.visibilityObserver.renderWhenVisible
|
||||
});
|
||||
|
||||
if (immediatelySelect) {
|
||||
this.removeSelectable = this.openmct.selection.selectable(
|
||||
|
@ -22,6 +22,7 @@
|
||||
<template>
|
||||
<div class="l-preview-window js-preview-window">
|
||||
<PreviewHeader
|
||||
ref="previewHeader"
|
||||
:current-view="currentViewProvider"
|
||||
:action-collection="actionCollection"
|
||||
:domain-object="domainObject"
|
||||
@ -48,7 +49,7 @@ export default {
|
||||
viewOptions: {
|
||||
type: Object,
|
||||
default() {
|
||||
return undefined;
|
||||
return {};
|
||||
}
|
||||
},
|
||||
existingView: {
|
||||
@ -147,6 +148,11 @@ export default {
|
||||
if (isExistingView) {
|
||||
this.viewContainer.appendChild(this.existingViewElement);
|
||||
} else {
|
||||
// in preview mode, we're always visible
|
||||
this.viewOptions.renderWhenVisible = (func) => {
|
||||
window.requestAnimationFrame(func);
|
||||
return true;
|
||||
};
|
||||
this.view.show(this.viewContainer, false, this.viewOptions);
|
||||
}
|
||||
|
||||
|
@ -279,6 +279,12 @@ export function getMockTelemetry(opts = {}) {
|
||||
return telemetry;
|
||||
}
|
||||
|
||||
// used to inject into tests that require a render
|
||||
export function renderWhenVisible(func) {
|
||||
func();
|
||||
return true;
|
||||
}
|
||||
|
||||
// copy objects a bit more easily
|
||||
function copyObj(obj) {
|
||||
return JSON.parse(JSON.stringify(obj));
|
||||
|
@ -23,36 +23,38 @@
|
||||
/**
|
||||
* Optimizes `requestAnimationFrame` calls to only execute when the element is visible in the viewport.
|
||||
*/
|
||||
export default class NicelyCalled {
|
||||
export default class VisibilityObserver {
|
||||
#element;
|
||||
#isIntersecting;
|
||||
#observer;
|
||||
#lastUnfiredFunc;
|
||||
lastUnfiredFunc;
|
||||
|
||||
/**
|
||||
* Constructs a NicelyCalled instance to manage visibility-based requestAnimationFrame calls.
|
||||
* Constructs a VisibilityObserver instance to manage visibility-based requestAnimationFrame calls.
|
||||
*
|
||||
* @param {HTMLElement} element - The DOM element to observe for visibility changes.
|
||||
* @throws {Error} If element is not provided.
|
||||
*/
|
||||
constructor(element) {
|
||||
if (!element) {
|
||||
throw new Error(`Nice visibility must be created with an element`);
|
||||
throw new Error(`VisibilityObserver must be created with an element`);
|
||||
}
|
||||
// set the id to some random 4 letters
|
||||
this.id = Math.random().toString(36).substring(2, 6);
|
||||
this.#element = element;
|
||||
this.#isIntersecting = true;
|
||||
this.isIntersecting = true;
|
||||
|
||||
this.#observer = new IntersectionObserver(this.#observerCallback);
|
||||
this.#observer.observe(this.#element);
|
||||
this.#lastUnfiredFunc = null;
|
||||
this.lastUnfiredFunc = null;
|
||||
this.renderWhenVisible = this.renderWhenVisible.bind(this);
|
||||
}
|
||||
|
||||
#observerCallback = ([entry]) => {
|
||||
if (entry.target === this.#element) {
|
||||
this.#isIntersecting = entry.isIntersecting;
|
||||
if (this.#isIntersecting && this.#lastUnfiredFunc) {
|
||||
window.requestAnimationFrame(this.#lastUnfiredFunc);
|
||||
this.#lastUnfiredFunc = null;
|
||||
this.isIntersecting = entry.isIntersecting;
|
||||
if (this.isIntersecting && this.lastUnfiredFunc) {
|
||||
window.requestAnimationFrame(this.lastUnfiredFunc);
|
||||
this.lastUnfiredFunc = null;
|
||||
}
|
||||
}
|
||||
};
|
||||
@ -65,12 +67,12 @@ export default class NicelyCalled {
|
||||
* @param {Function} func - The function to execute.
|
||||
* @returns {boolean} True if the function was executed immediately, false otherwise.
|
||||
*/
|
||||
execute(func) {
|
||||
if (this.#isIntersecting) {
|
||||
renderWhenVisible(func) {
|
||||
if (this.isIntersecting) {
|
||||
window.requestAnimationFrame(func);
|
||||
return true;
|
||||
} else {
|
||||
this.#lastUnfiredFunc = func;
|
||||
this.lastUnfiredFunc = func;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@ -81,8 +83,8 @@ export default class NicelyCalled {
|
||||
destroy() {
|
||||
this.#observer.unobserve(this.#element);
|
||||
this.#element = null;
|
||||
this.#isIntersecting = null;
|
||||
this.isIntersecting = null;
|
||||
this.#observer = null;
|
||||
this.#lastUnfiredFunc = null;
|
||||
this.lastUnfiredFunc = null;
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user