56 lines
2.4 KiB
JavaScript
56 lines
2.4 KiB
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);
|
|
}
|
|
}); |