diff --git a/src/plugins/d3/__stories__/bar-x/category.stories.tsx b/src/plugins/d3/__stories__/bar-x/category.stories.tsx index 27204750..6c0482df 100644 --- a/src/plugins/d3/__stories__/bar-x/category.stories.tsx +++ b/src/plugins/d3/__stories__/bar-x/category.stories.tsx @@ -13,7 +13,7 @@ const Template: Story = () => { const chartkitRef = React.useRef(); const data: ChartKitWidgetData = { legend: {enabled: true}, - tooltip: {enabled: false}, + tooltip: {enabled: true}, yAxis: [ { type: 'linear', diff --git a/src/plugins/d3/__stories__/scatter/LinearCategories.stories.tsx b/src/plugins/d3/__stories__/scatter/LinearCategories.stories.tsx index 3793e70e..5d9d487b 100644 --- a/src/plugins/d3/__stories__/scatter/LinearCategories.stories.tsx +++ b/src/plugins/d3/__stories__/scatter/LinearCategories.stories.tsx @@ -144,7 +144,7 @@ const Template: Story = () => { } return ( -
+
); diff --git a/src/plugins/d3/renderer/D3Widget.tsx b/src/plugins/d3/renderer/D3Widget.tsx index 6a96130f..1ce3cc26 100644 --- a/src/plugins/d3/renderer/D3Widget.tsx +++ b/src/plugins/d3/renderer/D3Widget.tsx @@ -8,6 +8,8 @@ import type {ChartKitProps, ChartKitWidgetRef} from '../../../types'; import {Chart} from './components'; type ChartDimensions = { + top: number; + left: number; width: number; height: number; }; @@ -20,8 +22,8 @@ const D3Widget = React.forwardRef { if (ref.current) { - const {width, height} = ref.current.getBoundingClientRect(); - setDimensions({width, height}); + const {top, left, width, height} = ref.current.getBoundingClientRect(); + setDimensions({top, left, width, height}); } }, []); @@ -57,9 +59,15 @@ const D3Widget = React.forwardRef +
{dimensions?.width && dimensions?.height && ( - + )}
); diff --git a/src/plugins/d3/renderer/components/Chart.tsx b/src/plugins/d3/renderer/components/Chart.tsx index 2b4e1a84..5f47c011 100644 --- a/src/plugins/d3/renderer/components/Chart.tsx +++ b/src/plugins/d3/renderer/components/Chart.tsx @@ -25,12 +25,16 @@ import './styles.scss'; const b = block('d3'); type Props = { + top: number; + left: number; width: number; height: number; data: ChartKitWidgetData; }; -export const Chart = ({width, height, data}: Props) => { +export const Chart = (props: Props) => { + const {top, left, width, height, data} = props; + // FIXME: add data validation const {series} = data; const svgRef = React.createRef(); const hasAxisRelatedSeries = series.data.some(isAxisRelatedSeries); @@ -58,6 +62,8 @@ export const Chart = ({width, height, data}: Props) => { tooltip, }); const {shapes} = useShapes({ + top, + left, boundsWidth, boundsHeight, series: chartSeries, diff --git a/src/plugins/d3/renderer/components/Tooltip/DefaultContent.tsx b/src/plugins/d3/renderer/components/Tooltip/DefaultContent.tsx index 3383c2bf..076bfcf7 100644 --- a/src/plugins/d3/renderer/components/Tooltip/DefaultContent.tsx +++ b/src/plugins/d3/renderer/components/Tooltip/DefaultContent.tsx @@ -1,6 +1,10 @@ import React from 'react'; -import type {ScatterSeriesData, TooltipHoveredData} from '../../../../../types/widget-data'; +import type { + ScatterSeriesData, + BarXSeriesData, + TooltipHoveredData, +} from '../../../../../types/widget-data'; import type {PreparedAxis} from '../../hooks'; @@ -31,6 +35,21 @@ export const DefaultContent = ({hovered, xAxis, yAxis}: Props) => {
); } + case 'bar-x': { + const barXData = data as BarXSeriesData; + const xRow = xAxis.type === 'category' ? barXData.category : barXData.x; + const yRow = yAxis.type === 'category' ? barXData.category : barXData.y; + return ( +
+
{xRow}
+
+ + {series.name}: {yRow} + +
+
+ ); + } default: { return null; } diff --git a/src/plugins/d3/renderer/hooks/useShapes/bar-x.tsx b/src/plugins/d3/renderer/hooks/useShapes/bar-x.tsx index ed40edec..ff04d31c 100644 --- a/src/plugins/d3/renderer/hooks/useShapes/bar-x.tsx +++ b/src/plugins/d3/renderer/hooks/useShapes/bar-x.tsx @@ -4,7 +4,7 @@ import {ChartScale} from '../useAxisScales'; import {OnSeriesMouseLeave, OnSeriesMouseMove} from '../useTooltip/types'; import {BarXSeries, BarXSeriesData} from '../../../../../types/widget-data'; import {block} from '../../../../../utils/cn'; -import {ScaleBand, ScaleLinear, ScaleTime} from 'd3'; +import {pointer, ScaleBand, ScaleLinear, ScaleTime} from 'd3'; const DEFAULT_BAR_RECT_WIDTH = 50; const DEFAULT_LINEAR_BAR_RECT_WIDTH = 20; @@ -13,6 +13,8 @@ const MIN_RECT_GAP = 1; const b = block('d3-bar'); type Args = { + top: number; + left: number; series: BarXSeries[]; xAxis: ChartOptions['xAxis']; xScale: ChartScale; @@ -20,6 +22,7 @@ type Args = { yScale: ChartScale; onSeriesMouseMove?: OnSeriesMouseMove; onSeriesMouseLeave?: OnSeriesMouseLeave; + svgContainer: SVGSVGElement | null; }; const getRectProperties = (args: { @@ -79,7 +82,18 @@ function minDiff(arr: number[]) { } export function prepareBarXSeries(args: Args) { - const {series, xAxis, xScale, yAxis, yScale, onSeriesMouseMove, onSeriesMouseLeave} = args; + const { + top, + left, + series, + xAxis, + xScale, + yAxis, + yScale, + onSeriesMouseMove, + onSeriesMouseLeave, + svgContainer, + } = args; const seriesData = series.map(({data}) => data).flat(2); const minPointDistance = minDiff(seriesData.map((item) => Number(item.x))); @@ -102,12 +116,14 @@ export function prepareBarXSeries(args: Args) { className={b('rect')} fill={item.color} {...rectProps} - onMouseMove={function () { + onMouseMove={function (e) { + const [x, y] = pointer(e, svgContainer); onSeriesMouseMove?.({ hovered: { data: point, series: item, }, + pointerPosition: [x - left, y - top], }); }} onMouseLeave={onSeriesMouseLeave} diff --git a/src/plugins/d3/renderer/hooks/useShapes/index.tsx b/src/plugins/d3/renderer/hooks/useShapes/index.tsx index a440c229..56cb3689 100644 --- a/src/plugins/d3/renderer/hooks/useShapes/index.tsx +++ b/src/plugins/d3/renderer/hooks/useShapes/index.tsx @@ -15,6 +15,8 @@ import {PieSeriesComponent} from './pie'; import './styles.scss'; type Args = { + top: number; + left: number; boundsWidth: number; boundsHeight: number; series: ChartSeries[]; @@ -29,6 +31,8 @@ type Args = { export const useShapes = (args: Args) => { const { + top, + left, boundsWidth, boundsHeight, series, @@ -40,6 +44,7 @@ export const useShapes = (args: Args) => { onSeriesMouseMove, onSeriesMouseLeave, } = args; + const shapes = React.useMemo(() => { const visibleSeries = getOnlyVisibleSeries(series); const groupedSeries = group(visibleSeries, (item) => item.type); @@ -51,6 +56,8 @@ export const useShapes = (args: Args) => { if (xScale && yScale) { acc.push( ...prepareBarXSeries({ + top, + left, series: chartSeries as BarXSeries[], xAxis, xScale, @@ -58,6 +65,7 @@ export const useShapes = (args: Args) => { yScale, onSeriesMouseMove, onSeriesMouseLeave, + svgContainer, }), ); } @@ -67,6 +75,8 @@ export const useShapes = (args: Args) => { if (xScale && yScale) { acc.push( ...prepareScatterSeries({ + top, + left, series: chartSeries as ScatterSeries[], xAxis, xScale, diff --git a/src/plugins/d3/renderer/hooks/useShapes/scatter.tsx b/src/plugins/d3/renderer/hooks/useShapes/scatter.tsx index 8fd26d60..a08d35d9 100644 --- a/src/plugins/d3/renderer/hooks/useShapes/scatter.tsx +++ b/src/plugins/d3/renderer/hooks/useShapes/scatter.tsx @@ -7,6 +7,8 @@ import {ScatterSeries, ScatterSeriesData} from '../../../../../types/widget-data import {block} from '../../../../../utils/cn'; type PrepareScatterSeriesArgs = { + top: number; + left: number; series: ScatterSeries[]; xAxis: ChartOptions['xAxis']; xScale: ChartScale; @@ -62,6 +64,8 @@ const getPointProperties = (args: { export function prepareScatterSeries(args: PrepareScatterSeriesArgs) { const { + top, + left, series, xAxis, xScale, @@ -96,12 +100,13 @@ export function prepareScatterSeries(args: PrepareScatterSeriesArgs) { fill={s.color} {...pointProps} onMouseMove={function (e) { + const [x, y] = pointer(e, svgContainer); onSeriesMouseMove?.({ hovered: { data: point, series: s, }, - pointerPosition: pointer(e, svgContainer), + pointerPosition: [x - left, y - top], }); }} onMouseLeave={onSeriesMouseLeave}