diff --git a/extensions/default/src/PanelMeasurementTable.js b/extensions/default/src/PanelMeasurementTable.js index cd83598b2..0967ac378 100644 --- a/extensions/default/src/PanelMeasurementTable.js +++ b/extensions/default/src/PanelMeasurementTable.js @@ -2,6 +2,7 @@ import React, { useEffect, useState } from 'react'; import PropTypes from 'prop-types'; import { MeasurementTable } from '@ohif/ui'; import { DicomMetadataStore } from '@ohif/core'; +import debounce from './debounce.js'; export default function PanelMeasurementTable({ servicesManager, @@ -11,11 +12,34 @@ export default function PanelMeasurementTable({ const [displayMeasurements, setDisplayMeasurements] = useState([]); useEffect(() => { - const measurements = MeasurementService.getMeasurements(); - const mappedMeasurements = measurements.map((m, index) => - _mapMeasurementToDisplay(m, index, MeasurementService.VALUE_TYPES) + const debouncedSetDisplayMeasurements = debounce( + setDisplayMeasurements, + 100 ); - setDisplayMeasurements(mappedMeasurements); + // ~~ Initial + setDisplayMeasurements(_getMappedMeasurements(MeasurementService)); + + // ~~ Subscription + const added = MeasurementService.EVENTS.MEASUREMENT_ADDED; + const updated = MeasurementService.EVENTS.MEASUREMENT_UPDATED; + const removed = MeasurementService.EVENTS.MEASUREMENT_REMOVED; + const subscriptions = []; + + [added, updated, removed].forEach(evt => { + subscriptions.push( + MeasurementService.subscribe(evt, () => { + debouncedSetDisplayMeasurements( + _getMappedMeasurements(MeasurementService) + ); + }).unsubscribe + ); + }); + + return () => { + subscriptions.forEach(unsub => { + unsub(); + }); + }; }, [MeasurementService]); // const activeMeasurementItem = 0; @@ -40,12 +64,23 @@ PanelMeasurementTable.propTypes = { services: PropTypes.shape({ MeasurementService: PropTypes.shape({ getMeasurements: PropTypes.func.isRequired, + subscribe: PropTypes.func.isRequired, + EVENTS: PropTypes.object.isRequired, VALUE_TYPES: PropTypes.object.isRequired, }).isRequired, }).isRequired, }).isRequired, }; +function _getMappedMeasurements(MeasurementService) { + const measurements = MeasurementService.getMeasurements(); + const mappedMeasurements = measurements.map((m, index) => + _mapMeasurementToDisplay(m, index, MeasurementService.VALUE_TYPES) + ); + + return mappedMeasurements; +} + function _mapMeasurementToDisplay(measurement, index, types) { const { id, diff --git a/extensions/default/src/debounce.js b/extensions/default/src/debounce.js new file mode 100644 index 000000000..83a16b099 --- /dev/null +++ b/extensions/default/src/debounce.js @@ -0,0 +1,21 @@ +// Returns a function, that, as long as it continues to be invoked, will not +// be triggered. The function will be called after it stops being called for +// N milliseconds. If `immediate` is passed, trigger the function on the +// leading edge, instead of the trailing. +function debounce(func, wait, immediate) { + var timeout; + return function() { + var context = this, + args = arguments; + var later = function() { + timeout = null; + if (!immediate) func.apply(context, args); + }; + var callNow = immediate && !timeout; + clearTimeout(timeout); + timeout = setTimeout(later, wait); + if (callNow) func.apply(context, args); + }; +} + +export default debounce;