Skip to content

Commit

Permalink
Funnel tooltip events (#4502)
Browse files Browse the repository at this point in the history
  • Loading branch information
PavelVanecek committed May 7, 2024
1 parent 96a819d commit 8b67483
Show file tree
Hide file tree
Showing 6 changed files with 215 additions and 40 deletions.
34 changes: 15 additions & 19 deletions src/cartesian/Bar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -151,16 +151,16 @@ function BarBackground(props: BarBackgroundProps) {

const { data, dataKey, background: backgroundFromProps, onAnimationStart, onAnimationEnd, allOtherBarProps } = props;

const onMouseEnterFromContext = useMouseEnterItemDispatch();
const onMouseLeaveFromContext = useMouseLeaveItemDispatch();
const onClickFromContext = useMouseClickItemDispatch();
const {
onMouseEnter: onMouseEnterFromProps,
onMouseLeave: onMouseLeaveFromProps,
onClick: onItemClickFromProps,
...restOfAllOtherProps
} = allOtherBarProps;

const onMouseEnterFromContext = useMouseEnterItemDispatch(onMouseEnterFromProps);
const onMouseLeaveFromContext = useMouseLeaveItemDispatch(onMouseLeaveFromProps);
const onClickFromContext = useMouseClickItemDispatch(onItemClickFromProps);
if (!backgroundFromProps) {
return null;
}
Expand All @@ -177,18 +177,16 @@ function BarBackground(props: BarBackgroundProps) {
}

const onMouseEnter = (e: React.MouseEvent<SVGPathElement, MouseEvent>) => {
onMouseEnterFromProps?.(entry, i, e);
// @ts-expect-error BarRectangleItem type definition says it's missing properties, but I can see them present in debugger!
onMouseEnterFromContext?.(entry, i);
onMouseEnterFromContext(entry, i, e);
};
const onMouseLeave = (e: React.MouseEvent<SVGPathElement, MouseEvent>) => {
onMouseLeaveFromProps?.(entry, i, e);
onMouseLeaveFromContext?.();
// @ts-expect-error BarRectangleItem type definition says it's missing properties, but I can see them present in debugger!
onMouseLeaveFromContext(entry, i, e);
};
const onClick = (e: React.MouseEvent<SVGPathElement, MouseEvent>) => {
onItemClickFromProps?.(entry, i, e);
// @ts-expect-error BarRectangleItem type definition says it's missing properties, but I can see them present in debugger!
onClickFromContext?.(entry, i);
onClickFromContext(entry, i, e);
};
const barRectangleProps: BarRectangleProps = {
option: backgroundFromProps,
Expand Down Expand Up @@ -227,17 +225,17 @@ function BarRectangles(props: BarRectanglesProps) {
const { data, shape, dataKey, activeBar } = props;

const { index: activeIndex } = useTooltipContext();

const onMouseEnterFromContext = useMouseEnterItemDispatch();
const onMouseLeaveFromContext = useMouseLeaveItemDispatch();
const onClickFromContext = useMouseClickItemDispatch();
const {
onMouseEnter: onMouseEnterFromProps,
onClick: onItemClickFromProps,
onMouseLeave: onMouseLeaveFromProps,
...restOfAllOtherProps
} = rest;

const onMouseEnterFromContext = useMouseEnterItemDispatch(onMouseEnterFromProps);
const onMouseLeaveFromContext = useMouseLeaveItemDispatch(onMouseLeaveFromProps);
const onClickFromContext = useMouseClickItemDispatch(onItemClickFromProps);

if (!data) {
return null;
}
Expand All @@ -258,18 +256,16 @@ function BarRectangles(props: BarRectanglesProps) {
onAnimationEnd,
};
const onMouseEnter = (e: React.MouseEvent<SVGPathElement, MouseEvent>) => {
onMouseEnterFromProps?.(entry, i, e);
// @ts-expect-error BarRectangleItem type definition says it's missing properties, but I can see them present in debugger!
onMouseEnterFromContext?.(entry, i);
onMouseEnterFromContext(entry, i, e);
};
const onMouseLeave = (e: React.MouseEvent<SVGPathElement, MouseEvent>) => {
onMouseLeaveFromProps?.(entry, i, e);
onMouseLeaveFromContext?.();
// @ts-expect-error BarRectangleItem type definition says it's missing properties, but I can see them present in debugger!
onMouseLeaveFromContext(entry, i, e);
};
const onClick = (e: React.MouseEvent<SVGPathElement, MouseEvent>) => {
onItemClickFromProps?.(entry, i, e);
// @ts-expect-error BarRectangleItem type definition says it's missing properties, but I can see them present in debugger!
onClickFromContext?.(entry, i);
onClickFromContext(entry, i, e);
};
return (
<Layer
Expand Down
10 changes: 5 additions & 5 deletions src/chart/generateCategoricalChart.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ import {
MouseClickItemDispatchContext,
MouseEnterItemDispatchContext,
MouseLeaveItemDispatchContext,
NoArgumentsAction,
TooltipPayloadType,
} from '../context/tooltipContext';

export interface MousePointer {
Expand Down Expand Up @@ -1582,7 +1582,7 @@ export const generateCategoricalChart = ({
* @param index 0-based index of the active graphical element
* @return undefined
*/
handleItemMouseEnter: ActivateTooltipAction = (el, index) => {
handleItemMouseEnter: ActivateTooltipAction<TooltipPayloadType> = (el, index) => {
this.setState(() => ({
activeTooltipIndex: index,
isTooltipActive: true,
Expand Down Expand Up @@ -1815,7 +1815,7 @@ export const generateCategoricalChart = ({
getGraphicalItemClickHandler(
tooltipEventType: TooltipEventType,
trigger: TooltipTrigger | undefined,
): ActivateTooltipAction | null {
): ActivateTooltipAction<TooltipPayloadType> | null {
if (tooltipEventType === 'axis') {
return null;
}
Expand All @@ -1829,7 +1829,7 @@ export const generateCategoricalChart = ({
getGraphicalItemMouseEnterHandler(
tooltipEventType: TooltipEventType,
trigger: TooltipTrigger | undefined,
): ActivateTooltipAction | null {
): ActivateTooltipAction<TooltipPayloadType> | null {
if (tooltipEventType === 'axis') {
return null;
}
Expand All @@ -1843,7 +1843,7 @@ export const generateCategoricalChart = ({
getGraphicalItemMouseLeaveHandler(
tooltipEventType: TooltipEventType,
trigger: TooltipTrigger | undefined,
): NoArgumentsAction | null {
): ActivateTooltipAction<TooltipPayloadType> | null {
if (tooltipEventType === 'axis') {
return null;
}
Expand Down
3 changes: 2 additions & 1 deletion src/chart/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import {
} from '../util/types';
import { AxisStackGroups } from '../util/ChartUtils';
import { BoundingBox } from '../util/useGetBoundingClientRect';
import { TooltipPayloadType } from '../context/tooltipContext';

export type AxisMap = {
[axisId: string]: BaseAxisProps;
Expand Down Expand Up @@ -56,7 +57,7 @@ export interface CategoricalChartState {
formattedGraphicalItems?: any;

/** active tooltip payload */
activePayload?: any[];
activePayload?: TooltipPayloadType;

tooltipAxisBandSize?: number;

Expand Down
54 changes: 44 additions & 10 deletions src/context/tooltipContext.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { createContext, useContext } from 'react';
import React, { createContext, useContext } from 'react';
import { ChartCoordinate, Coordinate } from '../util/types';

export type TooltipContextValue = {
Expand All @@ -23,17 +23,51 @@ export const TooltipContextProvider = TooltipContext.Provider;

export const useTooltipContext = () => useContext(TooltipContext);

export type ActivateTooltipAction = (
tooltipInfo: { tooltipPayload: any; tooltipPosition: Coordinate; cx: number; cy: number },
export type TooltipPayloadType = any[];

type TooltipTriggerInfo<T extends TooltipPayloadType> = {
tooltipPayload: T;
tooltipPosition: Coordinate;
cx: number;
cy: number;
};

export type ActivateTooltipAction<T extends TooltipPayloadType> = (
tooltipInfo: TooltipTriggerInfo<T>,
index: number,
event: React.MouseEvent<SVGElement>,
) => void;

export type NoArgumentsAction = () => void;
export const MouseEnterItemDispatchContext = createContext<ActivateTooltipAction<TooltipPayloadType> | null>(null);
export const MouseLeaveItemDispatchContext = createContext<ActivateTooltipAction<TooltipPayloadType> | null>(null);
export const MouseClickItemDispatchContext = createContext<ActivateTooltipAction<TooltipPayloadType> | null>(null);

export const MouseEnterItemDispatchContext = createContext<ActivateTooltipAction | null>(null);
export const MouseLeaveItemDispatchContext = createContext<NoArgumentsAction | null>(null);
export const MouseClickItemDispatchContext = createContext<ActivateTooltipAction | null>(null);
export const useMouseEnterItemDispatch = <T extends TooltipPayloadType>(
onMouseEnterFromProps: undefined | ActivateTooltipAction<T>,
): ActivateTooltipAction<T> => {
const onMouseEnterFromContext: undefined | ActivateTooltipAction<T> = useContext(MouseEnterItemDispatchContext);
return (data: TooltipTriggerInfo<T>, index: number, event: React.MouseEvent<SVGElement>) => {
onMouseEnterFromProps?.(data, index, event);
onMouseEnterFromContext?.(data, index, event);
};
};

export const useMouseEnterItemDispatch = () => useContext(MouseEnterItemDispatchContext);
export const useMouseLeaveItemDispatch = () => useContext(MouseLeaveItemDispatchContext);
export const useMouseClickItemDispatch = () => useContext(MouseClickItemDispatchContext);
export const useMouseLeaveItemDispatch = <T extends TooltipPayloadType>(
onMouseLeaveFromProps: undefined | ActivateTooltipAction<T>,
): ActivateTooltipAction<T> => {
const onMouseLeaveFromContext: undefined | ActivateTooltipAction<T> = useContext(MouseLeaveItemDispatchContext);
return (data: TooltipTriggerInfo<T>, index: number, event: React.MouseEvent<SVGElement>) => {
onMouseLeaveFromProps?.(data, index, event);
onMouseLeaveFromContext?.(data, index, event);
};
};

export const useMouseClickItemDispatch = <T extends TooltipPayloadType>(
onMouseClickFromProps: undefined | ActivateTooltipAction<T>,
): undefined | ActivateTooltipAction<T> => {
const onMouseClickFromContext: undefined | ActivateTooltipAction<T> = useContext(MouseClickItemDispatchContext);
return (data: TooltipTriggerInfo<T>, index: number, event: React.MouseEvent<SVGElement>) => {
onMouseClickFromProps?.(data, index, event);
onMouseClickFromContext?.(data, index, event);
};
};
34 changes: 32 additions & 2 deletions src/numberAxis/Funnel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,12 @@ import {
ActiveShape,
} from '../util/types';
import { FunnelTrapezoid, FunnelTrapezoidProps } from '../util/FunnelUtils';
import { useTooltipContext } from '../context/tooltipContext';
import {
useMouseClickItemDispatch,
useMouseEnterItemDispatch,
useMouseLeaveItemDispatch,
useTooltipContext,
} from '../context/tooltipContext';

export interface FunnelTrapezoidItem extends TrapezoidProps {
value?: number | string;
Expand Down Expand Up @@ -90,6 +95,16 @@ type FunnelTrapezoidsProps = {
function FunnelTrapezoids(props: FunnelTrapezoidsProps) {
const { trapezoids, shape, activeShape, allOtherFunnelProps } = props;
const { index: activeIndex } = useTooltipContext();
const {
onMouseEnter: onMouseEnterFromProps,
onClick: onItemClickFromProps,
onMouseLeave: onMouseLeaveFromProps,
...restOfAllOtherProps
} = allOtherFunnelProps;

const onMouseEnterFromContext = useMouseEnterItemDispatch(onMouseEnterFromProps);
const onMouseLeaveFromContext = useMouseLeaveItemDispatch(onMouseLeaveFromProps);
const onClickFromContext = useMouseClickItemDispatch(onItemClickFromProps);

return trapezoids.map((entry, i) => {
const isActiveIndex = activeShape && activeIndex === i;
Expand All @@ -100,11 +115,26 @@ function FunnelTrapezoids(props: FunnelTrapezoidsProps) {
isActive: isActiveIndex,
stroke: entry.stroke,
};
const onMouseEnter = (e: React.MouseEvent<SVGPathElement, MouseEvent>) => {
// @ts-expect-error the types need a bit of attention
onMouseEnterFromContext(entry, i, e);
};
const onMouseLeave = (e: React.MouseEvent<SVGPathElement, MouseEvent>) => {
// @ts-expect-error the types need a bit of attention
onMouseLeaveFromContext(entry, i, e);
};
const onClick = (e: React.MouseEvent<SVGPathElement, MouseEvent>) => {
// @ts-expect-error the types need a bit of attention
onClickFromContext(entry, i, e);
};

return (
<Layer
className="recharts-funnel-trapezoid"
{...adaptEventsOfChild(allOtherFunnelProps, entry, i)}
{...adaptEventsOfChild(restOfAllOtherProps, entry, i)}
onMouseEnter={onMouseEnter}
onMouseLeave={onMouseLeave}
onClick={onClick}
key={`trapezoid-${entry?.x}-${entry?.y}-${entry?.name}-${entry?.value}`}
role="img"
>
Expand Down
Loading

0 comments on commit 8b67483

Please sign in to comment.