recharts
Version:
React charts
56 lines (54 loc) • 2.47 kB
JavaScript
import { createAction, createListenerMiddleware } from '@reduxjs/toolkit';
import { selectActiveLabel, selectActiveTooltipCoordinate, selectActiveTooltipDataKey, selectActiveTooltipIndex, selectIsTooltipActive } from './selectors/tooltipSelectors';
export var externalEventAction = createAction('externalEvent');
export var externalEventsMiddleware = createListenerMiddleware();
/*
* We need a Map keyed by event type because this middleware handles MULTIPLE different event types
* (click, mouseenter, mouseleave, mousedown, mouseup, contextmenu, dblclick, touchstart, touchmove, touchend)
* from the same DOM element. Different event types should NOT cancel each other's animation frames.
* For example, a click event and a mousemove event can happen in quick succession and both should be processed.
* This is different from mouseMoveMiddleware which only handles one event type and uses a single rafId.
*/
var rafIdMap = new Map();
externalEventsMiddleware.startListening({
actionCreator: externalEventAction,
effect: (action, listenerApi) => {
var {
handler,
reactEvent
} = action.payload;
if (handler == null) {
return;
}
reactEvent.persist();
var eventType = reactEvent.type;
// Cancel any pending animation frame for this event type
var existingRafId = rafIdMap.get(eventType);
if (existingRafId !== undefined) {
cancelAnimationFrame(existingRafId);
}
var rafId = requestAnimationFrame(() => {
try {
/*
* Here it is important that we get the latest state inside the animation frame callback,
* not from the outer scope, because there may have been other actions dispatched
* between the time the event was fired and the animation frame callback is executed.
* One of those actions is the one that actually sets the active tooltip state!
*/
var state = listenerApi.getState();
var nextState = {
activeCoordinate: selectActiveTooltipCoordinate(state),
activeDataKey: selectActiveTooltipDataKey(state),
activeIndex: selectActiveTooltipIndex(state),
activeLabel: selectActiveLabel(state),
activeTooltipIndex: selectActiveTooltipIndex(state),
isTooltipActive: selectIsTooltipActive(state)
};
handler(nextState, reactEvent);
} finally {
rafIdMap.delete(eventType);
}
});
rafIdMap.set(eventType, rafId);
}
});