diff --git a/src/plugins/d3/renderer/components/AxisX.tsx b/src/plugins/d3/renderer/components/AxisX.tsx index 17cdade3..f070dbc3 100644 --- a/src/plugins/d3/renderer/components/AxisX.tsx +++ b/src/plugins/d3/renderer/components/AxisX.tsx @@ -1,6 +1,6 @@ import React from 'react'; import {axisBottom, select} from 'd3'; -import type {AxisScale, AxisDomain, Selection} from 'd3'; +import type {AxisScale, AxisDomain} from 'd3'; import {block} from '../../../../utils/cn'; @@ -17,26 +17,6 @@ type Props = { scale: ChartScale; }; -// Note: this method do not prepared for rotated labels -const removeOverlappingXTicks = (axis: Selection<SVGGElement, unknown, null, undefined>) => { - const a = axis.selectAll('g.tick').nodes(); - - if (a.length <= 1) { - return; - } - - for (let i = 0, x = 0; i < a.length; i++) { - const node = a[i] as Element; - const r = node.getBoundingClientRect(); - - if (r.left < x) { - node?.parentNode?.removeChild(node); - } else { - x = r.right + EMPTY_SPACE_BETWEEN_LABELS; - } - } -}; - // FIXME: add overflow ellipsis for the labels that out of boundaries export const AxisX = ({axis, width, height, scale}: Props) => { const ref = React.useRef<SVGGElement>(null); @@ -102,7 +82,20 @@ export const AxisX = ({axis, width, height, scale}: Props) => { .text(axis.title.text); } - removeOverlappingXTicks(svgElement); + let elementX = 0; + svgElement + .selectAll('.tick') + .filter(function () { + const node = this as unknown as Element; + const r = node.getBoundingClientRect(); + + if (r.left < elementX) { + return true; + } + elementX = r.right + EMPTY_SPACE_BETWEEN_LABELS; + return false; + }) + .remove(); }, [axis, width, height, scale]); return <g ref={ref} />; diff --git a/src/plugins/d3/renderer/hooks/useAxisScales/index.ts b/src/plugins/d3/renderer/hooks/useAxisScales/index.ts index 72bc7852..b57d48ff 100644 --- a/src/plugins/d3/renderer/hooks/useAxisScales/index.ts +++ b/src/plugins/d3/renderer/hooks/useAxisScales/index.ts @@ -44,17 +44,16 @@ const filterCategoriesByVisibleSeries = (args: { }) => { const {axisDirection, categories, series} = args; - return categories.filter((category) => { - return series.some((s) => { - return ( - isSeriesWithCategoryValues(s) && - s.data.some((d) => { - const dataCategory = getDataCategoryValue({axisDirection, categories, data: d}); - return dataCategory === category; - }) - ); - }); + const visibleCategories = new Set(); + series.forEach((s) => { + if (isSeriesWithCategoryValues(s)) { + s.data.forEach((d) => { + visibleCategories.add(getDataCategoryValue({axisDirection, categories, data: d})); + }); + } }); + + return categories.filter((c) => visibleCategories.has(c)); }; const createScales = (args: Args) => { diff --git a/src/plugins/d3/renderer/hooks/useShapes/scatter.tsx b/src/plugins/d3/renderer/hooks/useShapes/scatter.tsx index 99a2ddae..54bbc17a 100644 --- a/src/plugins/d3/renderer/hooks/useShapes/scatter.tsx +++ b/src/plugins/d3/renderer/hooks/useShapes/scatter.tsx @@ -88,18 +88,19 @@ export function ScatterSeriesShape(props: ScatterSeriesShapeProps) { } const svgElement = select(ref.current); - svgElement.selectAll('*').remove(); const preparedData = xAxis.type === 'category' || yAxis[0]?.type === 'category' ? series.data : prepareLinearScatterData(series.data); svgElement - .selectAll('allPoints') + .selectAll('circle') .data(preparedData) - .enter() - .append('circle') - .attr('class', b('point')) + .join( + (enter) => enter.append('circle').attr('class', b('point')), + (update) => update, + (exit) => exit.remove(), + ) .attr('fill', (d) => d.color || series.color || '') .attr('r', (d) => d.radius || DEFAULT_SCATTER_POINT_RADIUS) .attr('cx', (d) => getCxAttr({point: d, xAxis, xScale}))