Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"changes": [
{
"comment": "fix(react-vchart): when use children to render customized tooltip, react-vchart should update\n\n",
"type": "none",
"packageName": "@visactor/vchart"
}
],
"packageName": "@visactor/vchart",
"email": "dingling112@gmail.com"
}
4 changes: 1 addition & 3 deletions packages/react-vchart/src/charts/BaseChart.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -149,7 +149,6 @@ const BaseChart: React.FC<Props> = React.forwardRef((props, ref) => {
const specFromChildren = useRef<Omit<ISpec, 'type' | 'data' | 'width' | 'height'>>(null);
const eventsBinded = React.useRef<BaseChartProps>(null);
const skipFunctionDiff = !!props.skipFunctionDiff;
const [tooltipNode, setTooltipNode] = useState<ReactNode>(null);

const parseSpec = (props: Props) => {
let spec: ISpec;
Expand All @@ -170,7 +169,7 @@ const BaseChart: React.FC<Props> = React.forwardRef((props, ref) => {
} as ISpec;
}

const tooltipSpec = initCustomTooltip(setTooltipNode, props, spec.tooltip);
const tooltipSpec = initCustomTooltip(props, spec.tooltip);
if (tooltipSpec) {
spec.tooltip = tooltipSpec;
}
Expand Down Expand Up @@ -295,7 +294,6 @@ const BaseChart: React.FC<Props> = React.forwardRef((props, ref) => {
</React.Fragment>
);
})}
{tooltipNode}
</ViewContext.Provider>
</RootChartContext.Provider>
);
Expand Down
61 changes: 61 additions & 0 deletions packages/react-vchart/src/components/tooltip/BaseTooltip.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import React from 'react';
import { isObject, pickWithout } from '@visactor/vutils';
import { VChart } from '@visactor/vchart';
import type { TooltipRender } from './interface';

export interface BaseTooltipProps {
id?: string | number;
tooltipRender?: TooltipRender;
children?: React.ReactNode;
}

type TooltipProps = BaseTooltipProps & { updateId?: number; componentId?: number };

export const createTooltip = <T extends TooltipProps>(
componentName: string,
specName: string,
registers?: (() => void)[]
) => {
if (registers && registers.length) {
VChart.useRegisters(registers);
}

// tooltip component 不支持 children,其他组件暂时也都不支持
const ignoreKeys = ['updateId', 'componentId', 'children'];

const Comp: React.FC<T> = (props: T) => {
const updateId = React.useRef<number>(props.updateId);
if (props.updateId !== updateId.current) {
// update triggered by chart when chart is rendered
updateId.current = props.updateId;
}

return null;
};

Comp.displayName = componentName;
(Comp as any).parseSpec = (props: T & { updateId?: number; componentId?: string }) => {
const newTooltipSpec: Partial<T> = pickWithout<T>(props, ignoreKeys);

if (!props.tooltipRender && props.children) {
newTooltipSpec.tooltipRender = (tooltipElement, actualTooltip, params) =>
React.Children.map(props.children, child =>
isObject(child)
? React.cloneElement(child as React.ReactElement<any, React.JSXElementConstructor<any>>, {
tooltipElement,
actualTooltip,
params
})
: child
);
}

return {
spec: newTooltipSpec,
specName,
isSingle: true
};
};

return Comp;
};
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { createComponent } from '../BaseComponent';
import { TooltipProps } from './interface';
import { createTooltip } from './BaseTooltip';
import type { TooltipProps } from './interface';
import { registerTooltip, registerCanvasTooltipHandler } from '@visactor/vchart';

export const CanvasTooltip = createComponent<TooltipProps>('CanvasTooltip', 'tooltip', null, true, [
export const CanvasTooltip = createTooltip<TooltipProps>('CanvasTooltip', 'tooltip', [
registerTooltip,
registerCanvasTooltipHandler
]);
9 changes: 3 additions & 6 deletions packages/react-vchart/src/components/tooltip/Tooltip.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,5 @@
import { createComponent } from '../BaseComponent';
import { TooltipProps } from './interface';
import { createTooltip } from './BaseTooltip';
import type { TooltipProps } from './interface';
import { registerTooltip, registerDomTooltipHandler } from '@visactor/vchart';

export const Tooltip = createComponent<TooltipProps>('Tooltip', 'tooltip', null, true, [
registerTooltip,
registerDomTooltipHandler
]);
export const Tooltip = createTooltip<TooltipProps>('Tooltip', 'tooltip', [registerTooltip, registerDomTooltipHandler]);
55 changes: 30 additions & 25 deletions packages/react-vchart/src/components/tooltip/util.tsx
Original file line number Diff line number Diff line change
@@ -1,38 +1,23 @@
import React from 'react';
import type { BaseChartProps } from '../../charts/BaseChart';
import { TooltipProps, TooltipRender } from './interface';
import { isObject } from '@visactor/vutils';
import { ITooltipSpec } from '@visactor/vchart';
import type { TooltipProps, TooltipRender } from './interface';
import type { ITooltipSpec } from '@visactor/vchart';
import { REACT_TOOLTIP_ClASS_NAME } from './constant';
import { createPortal } from 'react-dom';
import { createRoot } from 'react-dom/client';
import { render as reactRender } from 'react-dom';

/** tooltip 自定义插槽 */
export const initCustomTooltip = (
setTooltipNode: React.Dispatch<React.SetStateAction<React.ReactNode>>,
props: BaseChartProps,
spec?: TooltipProps
) => {
let render: TooltipRender = undefined;
export const initCustomTooltip = (props: BaseChartProps, spec?: TooltipProps) => {
let render: TooltipRender;
if (spec?.tooltipRender) {
render = spec.tooltipRender;
delete spec.tooltipRender;
} else if (spec?.children) {
render = (tooltipElement, actualTooltip, params) =>
React.Children.map(spec.children, child =>
isObject(child)
? React.cloneElement(child as React.ReactElement<any, React.JSXElementConstructor<any>>, {
tooltipElement,
actualTooltip,
params
})
: child
);
} else if (props.tooltipRender) {
render = props.tooltipRender;
}

if (render) {
let reserve: boolean = undefined;
let reserve: boolean;
if (spec?.reserveDefaultTooltip) {
reserve = spec.reserveDefaultTooltip;
delete spec.reserveDefaultTooltip;
Expand All @@ -58,9 +43,29 @@ export const initCustomTooltip = (
}
}
}
setTooltipNode(
createPortal(<div className={REACT_TOOLTIP_ClASS_NAME}>{render(el, actualTooltip, params)}</div>, el)
);

let container = el.querySelector(`.${REACT_TOOLTIP_ClASS_NAME}`);
if (!container) {
// eslint-disable-next-line no-undef
container = document.createElement('div');
container.className = REACT_TOOLTIP_ClASS_NAME;
el.appendChild(container);
}
const element = render(el, actualTooltip, params);
const finalElement = React.isValidElement(element) ? element : <React.Fragment>{element}</React.Fragment>;

if (createRoot) {
if ((container as any).reactRoot) {
(container as any).reactRoot.render(finalElement);
} else {
const root = createRoot(container);
(container as any).reactRoot = root;
root.render(finalElement);
}
} else {
// react 17 以及以下
reactRender(finalElement, container);
}
}
} as ITooltipSpec;
}
Expand Down
Loading