| 1 | import { createAction, createListenerMiddleware } from '@reduxjs/toolkit';
|
|---|
| 2 | import { selectActiveLabel, selectActiveTooltipCoordinate, selectActiveTooltipDataKey, selectActiveTooltipIndex, selectIsTooltipActive } from './selectors/tooltipSelectors';
|
|---|
| 3 | export var externalEventAction = createAction('externalEvent');
|
|---|
| 4 | export var externalEventsMiddleware = createListenerMiddleware();
|
|---|
| 5 |
|
|---|
| 6 | /*
|
|---|
| 7 | * We need a Map keyed by event type because this middleware handles MULTIPLE different event types
|
|---|
| 8 | * (click, mouseenter, mouseleave, mousedown, mouseup, contextmenu, dblclick, touchstart, touchmove, touchend)
|
|---|
| 9 | * from the same DOM element. Different event types should NOT cancel each other's animation frames.
|
|---|
| 10 | * For example, a click event and a mousemove event can happen in quick succession and both should be processed.
|
|---|
| 11 | * This is different from mouseMoveMiddleware which only handles one event type and uses a single rafId.
|
|---|
| 12 | */
|
|---|
| 13 | var rafIdMap = new Map();
|
|---|
| 14 | externalEventsMiddleware.startListening({
|
|---|
| 15 | actionCreator: externalEventAction,
|
|---|
| 16 | effect: (action, listenerApi) => {
|
|---|
| 17 | var {
|
|---|
| 18 | handler,
|
|---|
| 19 | reactEvent
|
|---|
| 20 | } = action.payload;
|
|---|
| 21 | if (handler == null) {
|
|---|
| 22 | return;
|
|---|
| 23 | }
|
|---|
| 24 | reactEvent.persist();
|
|---|
| 25 | var eventType = reactEvent.type;
|
|---|
| 26 |
|
|---|
| 27 | // Cancel any pending animation frame for this event type
|
|---|
| 28 | var existingRafId = rafIdMap.get(eventType);
|
|---|
| 29 | if (existingRafId !== undefined) {
|
|---|
| 30 | cancelAnimationFrame(existingRafId);
|
|---|
| 31 | }
|
|---|
| 32 | var rafId = requestAnimationFrame(() => {
|
|---|
| 33 | try {
|
|---|
| 34 | /*
|
|---|
| 35 | * Here it is important that we get the latest state inside the animation frame callback,
|
|---|
| 36 | * not from the outer scope, because there may have been other actions dispatched
|
|---|
| 37 | * between the time the event was fired and the animation frame callback is executed.
|
|---|
| 38 | * One of those actions is the one that actually sets the active tooltip state!
|
|---|
| 39 | */
|
|---|
| 40 | var state = listenerApi.getState();
|
|---|
| 41 | var nextState = {
|
|---|
| 42 | activeCoordinate: selectActiveTooltipCoordinate(state),
|
|---|
| 43 | activeDataKey: selectActiveTooltipDataKey(state),
|
|---|
| 44 | activeIndex: selectActiveTooltipIndex(state),
|
|---|
| 45 | activeLabel: selectActiveLabel(state),
|
|---|
| 46 | activeTooltipIndex: selectActiveTooltipIndex(state),
|
|---|
| 47 | isTooltipActive: selectIsTooltipActive(state)
|
|---|
| 48 | };
|
|---|
| 49 | handler(nextState, reactEvent);
|
|---|
| 50 | } finally {
|
|---|
| 51 | rafIdMap.delete(eventType);
|
|---|
| 52 | }
|
|---|
| 53 | });
|
|---|
| 54 | rafIdMap.set(eventType, rafId);
|
|---|
| 55 | }
|
|---|
| 56 | }); |
|---|