Compare commits

...

4 Commits

18 changed files with 147 additions and 115 deletions

View File

@ -48,6 +48,8 @@
</template>
<script>
import throttle from '../../../utils/throttle';
const waitForMs = 1000;
const CONTEXT_MENU_ACTIONS = ['viewDatumAction', 'viewHistoricalData', 'remove'];
const BLANK_VALUE = '---';
@ -189,6 +191,7 @@ export default {
}
},
async mounted() {
this.updateView = throttle(this.updateView.bind(this), waitForMs);
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);
@ -241,13 +244,14 @@ export default {
methods: {
updateView() {
if (!this.updatingView) {
this.updatingView = this.renderWhenVisible(() => {
this.timestamp = this.getParsedTimestamp(this.latestDatum);
this.datum = this.latestDatum;
this.updatingView = false;
});
this.updatingView = this.renderWhenVisible(this.updateDatum);
}
},
updateDatum() {
this.timestamp = this.getParsedTimestamp(this.latestDatum);
this.datum = this.latestDatum;
this.updatingView = false;
},
clickedRow(event) {
if (this.openmct.editor.isEditing()) {
event.preventDefault();

View File

@ -39,7 +39,9 @@
<script>
import moment from 'moment';
import momentTimezone from 'moment-timezone';
import raf from 'utils/raf';
import throttle from '../../../utils/throttle';
const waitForMs = 1000;
export default {
inject: ['openmct', 'domainObject'],
@ -88,7 +90,7 @@ export default {
this.configuration = configuration;
}
);
this.tick = raf(this.tick);
this.tick = throttle(this.tick.bind(this), waitForMs);
this.openmct.time.on('tick', this.tick);
},
beforeUnmount() {

View File

@ -30,7 +30,9 @@
<script>
import moment from 'moment';
import raf from 'utils/raf';
import throttle from '../../../utils/throttle';
const waitForMs = 1000;
export default {
inject: ['openmct'],
@ -46,7 +48,7 @@ export default {
};
},
mounted() {
this.tick = raf(this.tick);
this.tick = throttle(this.tick.bind(this), waitForMs);
this.openmct.time.on('tick', this.tick);
this.tick(this.timeTextValue);
},

View File

@ -74,6 +74,8 @@ import {
import stalenessMixin from '@/ui/mixins/staleness-mixin';
import tooltipHelpers from '../../../api/tooltips/tooltipMixins';
import throttle from '../../../utils/throttle';
const waitForMs = 1000;
import conditionalStylesMixin from '../mixins/objectStyles-mixin';
import LayoutFrame from './LayoutFrame.vue';
@ -236,6 +238,7 @@ export default {
}
},
mounted() {
this.updateView = throttle(this.updateView.bind(this), waitForMs);
this.getAndSetObject();
this.status = this.openmct.status.get(this.item.identifier);
@ -294,12 +297,13 @@ export default {
},
updateView() {
if (!this.updatingView) {
this.updatingView = this.renderWhenVisible(() => {
this.datum = this.latestDatum;
this.updatingView = false;
});
this.updatingView = this.renderWhenVisible(this.updateDatum);
}
},
updateDatum() {
this.datum = this.latestDatum;
this.updatingView = false;
},
refreshData(bounds, isTick) {
if (!isTick) {
this.latestDatum = undefined;

View File

@ -733,11 +733,12 @@ export default {
return;
}
this.isRendering = this.renderWhenVisible(() => {
this.isRendering = false;
this.isRendering = this.renderWhenVisible(this.updateCurrentValue);
},
updateCurrentValue() {
this.isRendering = false;
this.curVal = this.round(this.formats[this.valueKey].format(this.datum), this.precision);
});
this.curVal = this.round(this.formats[this.valueKey].format(this.datum), this.precision);
},
valToPercent(vValue) {
// Used by dial

View File

@ -322,14 +322,14 @@ export default {
},
watch: {
sizedImageDimensions() {
this.debounceResizeSvg();
this.throttleResizeSvg();
}
},
mounted() {
this.debounceResizeSvg = throttle(this.resizeSvg, 100);
this.throttleResizeSvg = throttle(this.resizeSvg.bind(this), 100);
this.$nextTick(() => {
this.debounceResizeSvg();
this.throttleResizeSvg();
});
},
methods: {

View File

@ -19,7 +19,8 @@
* this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information.
*****************************************************************************/
import raf from '@/utils/raf';
import throttle from '../../utils/throttle';
const waitForMs = 1000;
export default class AbstractStatusIndicator {
#popupComponent;
@ -36,8 +37,7 @@ export default class AbstractStatusIndicator {
this.showPopup = this.showPopup.bind(this);
this.clearPopup = this.clearPopup.bind(this);
this.positionBox = this.positionBox.bind(this);
this.positionBox = raf(this.positionBox);
this.positionBox = throttle(this.positionBox.bind(this), waitForMs);
this.#indicator = this.createIndicator();
this.#popupComponent = this.createPopupComponent();

View File

@ -284,6 +284,7 @@ import CSVExporter from '../../../exporters/CSVExporter.js';
import ProgressBar from '../../../ui/components/ProgressBar.vue';
import Search from '../../../ui/components/SearchComponent.vue';
import ToggleSwitch from '../../../ui/components/ToggleSwitch.vue';
import throttle from '../../../utils/throttle';
import SizingRow from './SizingRow.vue';
import TableColumnHeader from './TableColumnHeader.vue';
import TableFooterIndicator from './TableFooterIndicator.vue';
@ -293,6 +294,7 @@ const VISIBLE_ROW_COUNT = 100;
const ROW_HEIGHT = 17;
const RESIZE_POLL_INTERVAL = 200;
const AUTO_SCROLL_TRIGGER_HEIGHT = 100;
const waitForMs = 1000;
export default {
components: {
@ -481,9 +483,9 @@ export default {
},
mounted() {
this.csvExporter = new CSVExporter();
this.rowsAdded = _.throttle(this.rowsAdded, 200);
this.rowsRemoved = _.throttle(this.rowsRemoved, 200);
this.scroll = _.throttle(this.scroll, 100);
this.rowsAdded = throttle(this.rowsAdded.bind(this), waitForMs);
this.rowsRemoved = throttle(this.rowsRemoved.bind(this), waitForMs);
this.scroll = throttle(this.scroll.bind(this), waitForMs);
if (!this.marking.useAlternateControlBar && !this.enableLegacyToolbar) {
this.$nextTick(() => {
@ -549,41 +551,42 @@ export default {
methods: {
updateVisibleRows() {
if (!this.updatingView) {
this.updatingView = this.renderWhenVisible(() => {
let start = 0;
let end = VISIBLE_ROW_COUNT;
let tableRows = this.table.tableRows.getRows();
let tableRowsLength = tableRows.length;
this.totalNumberOfRows = tableRowsLength;
if (tableRowsLength < VISIBLE_ROW_COUNT) {
end = tableRowsLength;
} else {
let firstVisible = this.calculateFirstVisibleRow();
let lastVisible = this.calculateLastVisibleRow();
let totalVisible = lastVisible - firstVisible;
let numberOffscreen = VISIBLE_ROW_COUNT - totalVisible;
start = firstVisible - Math.floor(numberOffscreen / 2);
end = lastVisible + Math.ceil(numberOffscreen / 2);
if (start < 0) {
start = 0;
end = Math.min(VISIBLE_ROW_COUNT, tableRowsLength);
} else if (end >= tableRowsLength) {
end = tableRowsLength;
start = end - VISIBLE_ROW_COUNT + 1;
}
}
this.rowOffset = start;
this.visibleRows = tableRows.slice(start, end);
this.updatingView = false;
});
this.updatingView = this.renderWhenVisible(this.setVisibleRows);
}
},
setVisibleRows() {
let start = 0;
let end = VISIBLE_ROW_COUNT;
let tableRows = this.table.tableRows.getRows();
let tableRowsLength = tableRows.length;
this.totalNumberOfRows = tableRowsLength;
if (tableRowsLength < VISIBLE_ROW_COUNT) {
end = tableRowsLength;
} else {
let firstVisible = this.calculateFirstVisibleRow();
let lastVisible = this.calculateLastVisibleRow();
let totalVisible = lastVisible - firstVisible;
let numberOffscreen = VISIBLE_ROW_COUNT - totalVisible;
start = firstVisible - Math.floor(numberOffscreen / 2);
end = lastVisible + Math.ceil(numberOffscreen / 2);
if (start < 0) {
start = 0;
end = Math.min(VISIBLE_ROW_COUNT, tableRowsLength);
} else if (end >= tableRowsLength) {
end = tableRowsLength;
start = end - VISIBLE_ROW_COUNT + 1;
}
}
this.rowOffset = start;
this.visibleRows = tableRows.slice(start, end);
this.updatingView = false;
},
calculateFirstVisibleRow() {
let scrollTop = this.scrollable.scrollTop;
@ -822,30 +825,31 @@ export default {
this.isDropTargetActive = isActive;
},
pollForResize() {
this.resizePollHandle = setInterval(() => {
this.renderWhenVisible(this.calculateTableSizeAndSetScrollTop.bind(this));
}, RESIZE_POLL_INTERVAL);
},
calculateTableSizeAndSetScrollTop() {
let el = this.$refs.root;
let width = el.clientWidth;
let height = el.clientHeight;
let scrollTop = this.scrollable.scrollTop;
this.resizePollHandle = setInterval(() => {
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?
// Need to preserve scroll position in this case.
if (this.autoScroll) {
this.scrollToBottom();
} else {
this.scrollable.scrollTop = scrollTop;
}
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?
// Need to preserve scroll position in this case.
if (this.autoScroll) {
this.scrollToBottom();
} else {
this.scrollable.scrollTop = scrollTop;
}
width = el.clientWidth;
height = el.clientHeight;
}
width = el.clientWidth;
height = el.clientHeight;
}
scrollTop = this.scrollable.scrollTop;
});
}, RESIZE_POLL_INTERVAL);
scrollTop = this.scrollable.scrollTop;
},
clearRowsAndRerender() {
this.visibleRows = [];

View File

@ -81,6 +81,7 @@ import {
REALTIME_MODE_KEY,
TIME_CONTEXT_EVENTS
} from '../../api/time/constants';
import throttle from '../../utils/throttle';
import ConductorAxis from './ConductorAxis.vue';
import ConductorClock from './ConductorClock.vue';
import ConductorInputsFixed from './ConductorInputsFixed.vue';
@ -155,8 +156,9 @@ export default {
document.addEventListener('keyup', this.handleKeyUp);
this.setTimeSystem(this.copy(this.openmct.time.getTimeSystem()));
this.handleNewBounds = throttle(this.handleNewBounds.bind(this), 1000);
this.openmct.time.on(TIME_CONTEXT_EVENTS.boundsChanged, _.throttle(this.handleNewBounds, 300));
this.openmct.time.on(TIME_CONTEXT_EVENTS.boundsChanged, this.handleNewBounds);
this.openmct.time.on(TIME_CONTEXT_EVENTS.timeSystemChanged, this.setTimeSystem);
this.openmct.time.on(TIME_CONTEXT_EVENTS.modeChanged, this.setMode);
},
@ -164,7 +166,7 @@ export default {
document.removeEventListener('keydown', this.handleKeyDown);
document.removeEventListener('keyup', this.handleKeyUp);
this.openmct.time.off(TIME_CONTEXT_EVENTS.boundsChanged, _.throttle(this.handleNewBounds, 300));
this.openmct.time.off(TIME_CONTEXT_EVENTS.boundsChanged, this.handleNewBounds);
this.openmct.time.off(TIME_CONTEXT_EVENTS.timeSystemChanged, this.setTimeSystem);
this.openmct.time.off(TIME_CONTEXT_EVENTS.modeChanged, this.setMode);
},

View File

@ -51,6 +51,7 @@
import _ from 'lodash';
import { TIME_CONTEXT_EVENTS } from '../../api/time/constants';
import throttle from '../../utils/throttle';
import TimePopupFixed from './TimePopupFixed.vue';
export default {
@ -123,7 +124,7 @@ export default {
}
},
mounted() {
this.handleNewBounds = _.throttle(this.handleNewBounds, 300);
this.handleNewBounds = throttle(this.handleNewBounds.bind(this), 1000);
this.setTimeSystem(JSON.parse(JSON.stringify(this.openmct.time.getTimeSystem())));
this.openmct.time.on(TIME_CONTEXT_EVENTS.timeSystemChanged, this.setTimeSystem);
this.setTimeContext();

View File

@ -58,6 +58,7 @@
import _ from 'lodash';
import { TIME_CONTEXT_EVENTS } from '../../api/time/constants';
import throttle from '../../utils/throttle';
import TimePopupRealtime from './TimePopupRealtime.vue';
const DEFAULT_DURATION_FORMATTER = 'duration';
@ -145,7 +146,7 @@ export default {
}
},
mounted() {
this.handleNewBounds = _.throttle(this.handleNewBounds, 300);
this.handleNewBounds = throttle(this.handleNewBounds.bind(this), 1000);
this.setTimeSystem(this.copy(this.openmct.time.getTimeSystem()));
this.openmct.time.on(TIME_CONTEXT_EVENTS.timeSystemChanged, this.setTimeSystem);
this.setTimeContext();

View File

@ -96,6 +96,7 @@
<script>
import _ from 'lodash';
import throttle from '../../utils/throttle';
import DatePicker from './DatePicker.vue';
const DEFAULT_DURATION_FORMATTER = 'duration';
@ -156,7 +157,7 @@ export default {
}
},
mounted() {
this.handleNewBounds = _.throttle(this.handleNewBounds, 300);
this.handleNewBounds = throttle(this.handleNewBounds.bind(this), 1000);
this.setTimeSystem(JSON.parse(JSON.stringify(this.openmct.time.getTimeSystem())));
},
beforeUnmount() {

View File

@ -19,7 +19,8 @@
* this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information.
*****************************************************************************/
import raf from '@/utils/raf';
import throttle from '../../utils/throttle';
const waitForMs = 1000;
export default {
inject: ['openmct', 'configuration'],
@ -32,7 +33,7 @@ export default {
};
},
mounted() {
this.positionBox = raf(this.positionBox);
this.positionBox = throttle(this.positionBox.bind(this), waitForMs);
this.timeConductorOptionsHolder = this.$el;
this.timeConductorOptionsHolder.addEventListener('click', this.showPopup);
},

View File

@ -20,7 +20,9 @@
* at runtime from the About dialog for additional information.
*****************************************************************************/
import debounce from '@/utils/debounce';
import raf from '@/utils/raf';
import throttle from '../../../utils/throttle';
const waitForMs = 1000;
export default {
data() {
@ -38,7 +40,7 @@ export default {
}
},
mounted() {
this.positionBox = debounce(raf(this.positionBox), 250);
this.positionBox = debounce(throttle(this.positionBox.bind(this), waitForMs), 250);
this.timeConductorOptionsHolder = this.$el;
this.timeConductorOptionsHolder.addEventListener('click', this.showPopup);
},

View File

@ -38,6 +38,7 @@ import { v4 as uuid } from 'uuid';
import { TIME_CONTEXT_EVENTS } from '../../api/time/constants';
import ListView from '../../ui/components/List/ListView.vue';
import { getPreciseDuration } from '../../utils/duration';
import throttle from '../../utils/throttle';
import { getValidatedData } from '../plan/util';
import { SORT_ORDER_OPTIONS } from './constants';
@ -116,7 +117,7 @@ export default {
},
mounted() {
this.isEditing = this.openmct.editor.isEditing();
this.updateTimestamp = _.throttle(this.updateTimestamp, 1000);
this.updateTimestamp = throttle(this.updateTimestamp.bind(this), 1000);
this.setTimeContext();
this.timestamp = this.timeContext.now();

View File

@ -44,13 +44,12 @@
</template>
<script>
import raf from 'utils/raf';
import throttle from '../../../utils/throttle';
const moment = require('moment-timezone');
const momentDurationFormatSetup = require('moment-duration-format');
const refreshRateSeconds = 2;
const waitForMs = 1000;
momentDurationFormatSetup(moment);
@ -176,8 +175,11 @@ export default {
this.triggerAction(`timer.${timerAction}`);
}
this.handleTick = raf(this.handleTick);
this.refreshTimerObject = throttle(this.refreshTimerObject, refreshRateSeconds * 1000);
this.handleTick = throttle(this.handleTick.bind(this), waitForMs);
this.refreshTimerObject = throttle(
this.refreshTimerObject.bind(this),
refreshRateSeconds * 1000
);
this.openmct.time.on('tick', this.handleTick);
this.viewActionsCollection = this.openmct.actions.getActionsCollection(

View File

@ -1,10 +1,11 @@
import raf from './raf';
import throttle from './throttle';
const waitForMs = 1000;
describe('The raf utility function', () => {
it('Throttles function calls that arrive in quick succession using Request Animation Frame', () => {
const unthrottledFunction = jasmine.createSpy('unthrottledFunction');
const throttledCallback = jasmine.createSpy('throttledCallback');
const throttledFunction = raf(throttledCallback);
const throttledFunction = throttle(throttledCallback, waitForMs);
for (let i = 0; i < 10; i++) {
unthrottledFunction();
@ -20,7 +21,7 @@ describe('The raf utility function', () => {
});
it('Only invokes callback once per animation frame', () => {
const throttledCallback = jasmine.createSpy('throttledCallback');
const throttledFunction = raf(throttledCallback);
const throttledFunction = throttle(throttledCallback, waitForMs);
for (let i = 0; i < 10; i++) {
throttledFunction();
@ -40,7 +41,7 @@ describe('The raf utility function', () => {
});
it('Invokes callback again if called in subsequent animation frame', () => {
const throttledCallback = jasmine.createSpy('throttledCallback');
const throttledFunction = raf(throttledCallback);
const throttledFunction = throttle(throttledCallback, waitForMs);
for (let i = 0; i < 10; i++) {
throttledFunction();

View File

@ -6,29 +6,32 @@
* @return {Function} Returns the new throttled function.
*/
export default function throttle(func, wait) {
let timeout;
let result;
let previous = 0;
let waiting = false;
let lastArgs = null;
// If the function was invoked while we were waiting, call it with those arguments
// If it wasn't called when we were waiting, we're done
function checkFunctionInvokedOrStopWaiting() {
if (lastArgs !== null) {
func(...lastArgs);
lastArgs = null;
setTimeout(checkFunctionInvokedOrStopWaiting, wait);
} else {
waiting = false;
}
}
return function (...args) {
const now = new Date().getTime();
const remaining = wait - (now - previous);
if (remaining <= 0 || remaining > wait) {
if (timeout) {
clearTimeout(timeout);
timeout = null;
}
previous = now;
result = func(...args);
} else if (!timeout) {
timeout = setTimeout(() => {
previous = new Date().getTime();
timeout = null;
result = func(...args);
}, remaining);
// if we're waiting (in between calls), store the arguments to use for when we call the function after we're done waiting
if (waiting) {
lastArgs = args;
return;
}
return result;
// if we're not waiting (between calls), call the function and wait
func(...args);
waiting = true;
// when we're done waiting, now check if the function was invoked while we were waiting
setTimeout(checkFunctionInvokedOrStopWaiting, wait);
};
}