From 4679585fb03853329eb15ee61a79c55a182fcb38 Mon Sep 17 00:00:00 2001 From: xile611 Date: Fri, 23 May 2025 15:48:59 +0800 Subject: [PATCH 1/2] fix(react-vchart): when use children to render customized tooltip, react-vchart should update --- .../react-vchart/src/charts/BaseChart.tsx | 4 +- .../src/components/tooltip/BaseTooltip.tsx | 61 +++++++++++++++++++ .../src/components/tooltip/CanvasTooltip.tsx | 6 +- .../src/components/tooltip/Tooltip.tsx | 9 +-- .../src/components/tooltip/util.tsx | 55 +++++++++-------- 5 files changed, 98 insertions(+), 37 deletions(-) create mode 100644 packages/react-vchart/src/components/tooltip/BaseTooltip.tsx diff --git a/packages/react-vchart/src/charts/BaseChart.tsx b/packages/react-vchart/src/charts/BaseChart.tsx index d9b0840317..377ab6986e 100644 --- a/packages/react-vchart/src/charts/BaseChart.tsx +++ b/packages/react-vchart/src/charts/BaseChart.tsx @@ -149,7 +149,6 @@ const BaseChart: React.FC = React.forwardRef((props, ref) => { const specFromChildren = useRef>(null); const eventsBinded = React.useRef(null); const skipFunctionDiff = !!props.skipFunctionDiff; - const [tooltipNode, setTooltipNode] = useState(null); const parseSpec = (props: Props) => { let spec: ISpec; @@ -170,7 +169,7 @@ const BaseChart: React.FC = React.forwardRef((props, ref) => { } as ISpec; } - const tooltipSpec = initCustomTooltip(setTooltipNode, props, spec.tooltip); + const tooltipSpec = initCustomTooltip(props, spec.tooltip); if (tooltipSpec) { spec.tooltip = tooltipSpec; } @@ -295,7 +294,6 @@ const BaseChart: React.FC = React.forwardRef((props, ref) => { ); })} - {tooltipNode} ); diff --git a/packages/react-vchart/src/components/tooltip/BaseTooltip.tsx b/packages/react-vchart/src/components/tooltip/BaseTooltip.tsx new file mode 100644 index 0000000000..62bf658dea --- /dev/null +++ b/packages/react-vchart/src/components/tooltip/BaseTooltip.tsx @@ -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 = ( + 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 = (props: T) => { + const updateId = React.useRef(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 = pickWithout(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>, { + tooltipElement, + actualTooltip, + params + }) + : child + ); + } + + return { + spec: newTooltipSpec, + specName, + isSingle: true + }; + }; + + return Comp; +}; diff --git a/packages/react-vchart/src/components/tooltip/CanvasTooltip.tsx b/packages/react-vchart/src/components/tooltip/CanvasTooltip.tsx index f64a3c7ed6..e822aaa5b2 100644 --- a/packages/react-vchart/src/components/tooltip/CanvasTooltip.tsx +++ b/packages/react-vchart/src/components/tooltip/CanvasTooltip.tsx @@ -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('CanvasTooltip', 'tooltip', null, true, [ +export const CanvasTooltip = createTooltip('CanvasTooltip', 'tooltip', [ registerTooltip, registerCanvasTooltipHandler ]); diff --git a/packages/react-vchart/src/components/tooltip/Tooltip.tsx b/packages/react-vchart/src/components/tooltip/Tooltip.tsx index 7802b596f6..1e2c662b34 100644 --- a/packages/react-vchart/src/components/tooltip/Tooltip.tsx +++ b/packages/react-vchart/src/components/tooltip/Tooltip.tsx @@ -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('Tooltip', 'tooltip', null, true, [ - registerTooltip, - registerDomTooltipHandler -]); +export const Tooltip = createTooltip('Tooltip', 'tooltip', [registerTooltip, registerDomTooltipHandler]); diff --git a/packages/react-vchart/src/components/tooltip/util.tsx b/packages/react-vchart/src/components/tooltip/util.tsx index cce89edba7..5c8cbcf672 100644 --- a/packages/react-vchart/src/components/tooltip/util.tsx +++ b/packages/react-vchart/src/components/tooltip/util.tsx @@ -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>, - 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>, { - 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; @@ -58,9 +43,29 @@ export const initCustomTooltip = ( } } } - setTooltipNode( - createPortal(
{render(el, actualTooltip, params)}
, 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 : {element}; + + 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; } From 177f5b5933426a93667ea4c0be3c57b2db418b46 Mon Sep 17 00:00:00 2001 From: xile611 Date: Fri, 23 May 2025 15:49:36 +0800 Subject: [PATCH 2/2] docs: update changlog of rush --- .../vchart/fix-tooltip-children_2025-05-23-07-49.json | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 common/changes/@visactor/vchart/fix-tooltip-children_2025-05-23-07-49.json diff --git a/common/changes/@visactor/vchart/fix-tooltip-children_2025-05-23-07-49.json b/common/changes/@visactor/vchart/fix-tooltip-children_2025-05-23-07-49.json new file mode 100644 index 0000000000..5ae4b01534 --- /dev/null +++ b/common/changes/@visactor/vchart/fix-tooltip-children_2025-05-23-07-49.json @@ -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" +} \ No newline at end of file