diff --git a/src/plugins/d3/examples/scatter/Basic.tsx b/src/plugins/d3/examples/scatter/Basic.tsx
index 1a3b79c6..a30daefb 100644
--- a/src/plugins/d3/examples/scatter/Basic.tsx
+++ b/src/plugins/d3/examples/scatter/Basic.tsx
@@ -1,7 +1,6 @@
import React from 'react';
-import {dateTime} from '@gravity-ui/date-utils';
import {ChartKit} from '../../../../components/ChartKit';
-import type {ChartKitWidgetData, ScatterSeries, ScatterSeriesData} from '../../../../types';
+import type {ChartKitWidgetData, ScatterSeries} from '../../../../types';
import {ExampleWrapper} from '../ExampleWrapper';
import nintendoGames from '../nintendoGames';
@@ -47,29 +46,6 @@ export const Basic = () => {
text: 'Release dates',
},
},
- tooltip: {
- renderer: (d) => {
- const point = d.hovered[0]?.data as ScatterSeriesData;
-
- if (!point) {
- return null;
- }
-
- const title = point.custom.title;
- const score = point.custom.user_score;
- const date = dateTime({input: point.custom.date}).format('DD MMM YYYY');
-
- return (
-
- {title}
-
- Release date: {date}
-
- User score: {score}
-
- );
- },
- },
};
return (
diff --git a/src/plugins/d3/renderer/components/Tooltip/TooltipTriggerArea.tsx b/src/plugins/d3/renderer/components/Tooltip/TooltipTriggerArea.tsx
index 2366de42..8dfdb3bc 100644
--- a/src/plugins/d3/renderer/components/Tooltip/TooltipTriggerArea.tsx
+++ b/src/plugins/d3/renderer/components/Tooltip/TooltipTriggerArea.tsx
@@ -9,6 +9,7 @@ import type {NodeWithD3Data} from '../../utils';
import {PreparedLineData} from '../../hooks/useShapes/line/types';
import {BarYSeriesData, LineSeriesData} from '../../../../../types';
import {PreparedBarYData} from '../../hooks/useShapes/bar-y/types';
+import get from 'lodash/get';
const THROTTLE_DELAY = 50;
@@ -119,7 +120,7 @@ export const TooltipTriggerArea = (args: Args) => {
const rectRef = React.useRef(null);
const xBarData = React.useMemo(() => {
const result = shapesData
- .filter((sd) => sd.series.type === 'bar-x')
+ .filter((sd) => get(sd, 'series.type') === 'bar-x')
.map((sd) => ({x: (sd as PreparedBarXData).x, data: sd}));
return sort(result, (item) => item.x);
@@ -127,13 +128,13 @@ export const TooltipTriggerArea = (args: Args) => {
const xLineData = React.useMemo(() => {
const result = shapesData
- .filter((sd) => ['line', 'area'].includes(sd.series.type))
+ .filter((sd) => ['line', 'area'].includes((sd as PreparedLineData).series.type))
.reduce((acc, sd) => {
return acc.concat(
(sd as PreparedLineData).points.map((d) => ({
x: d.x,
data: d.data,
- series: sd.series,
+ series: d.series,
})),
);
}, [] as XLineData[]);
@@ -142,7 +143,7 @@ export const TooltipTriggerArea = (args: Args) => {
}, [shapesData]);
const barYData = React.useMemo(() => {
- const barYShapeData = shapesData.filter((sd) => sd.series.type === 'bar-y');
+ const barYShapeData = shapesData.filter((sd) => get(sd, 'series.type') === 'bar-y');
const result = Array.from(group(barYShapeData, (sd) => (sd as PreparedBarYData).y)).map(
([y, shapes]) => {
const yValue = y + (shapes[0] as PreparedBarYData).height / 2;
@@ -156,7 +157,7 @@ export const TooltipTriggerArea = (args: Args) => {
return {
x: preparedData.x + preparedData.width,
data: preparedData.data,
- series: shape.series,
+ series: preparedData.series,
};
}),
(item) => item.x,
diff --git a/src/plugins/d3/renderer/hooks/useSeries/__tests__/prepare-line-series.test.ts b/src/plugins/d3/renderer/hooks/useSeries/__tests__/prepare-line-series.test.ts
index 7d3e7009..633c3bb0 100644
--- a/src/plugins/d3/renderer/hooks/useSeries/__tests__/prepare-line-series.test.ts
+++ b/src/plugins/d3/renderer/hooks/useSeries/__tests__/prepare-line-series.test.ts
@@ -1,4 +1,4 @@
-import {DEFAULT_MARKER, prepareLineSeries} from '../prepare-line-series';
+import {DEFAULT_MARKER, prepareLineSeries} from '../prepare-line';
import {scaleOrdinal} from 'd3';
import type {LineSeries} from '../../../../../../types';
import type {PreparedLegend} from '../types';
diff --git a/src/plugins/d3/renderer/hooks/useSeries/constants.ts b/src/plugins/d3/renderer/hooks/useSeries/constants.ts
index 792f566f..8996bb37 100644
--- a/src/plugins/d3/renderer/hooks/useSeries/constants.ts
+++ b/src/plugins/d3/renderer/hooks/useSeries/constants.ts
@@ -1,4 +1,5 @@
import type {BaseTextStyle, Halo} from '../../../../../types';
+import {PointMarkerOptions} from '../../../../../types/widget-data/marker';
export const DEFAULT_LEGEND_SYMBOL_SIZE = 8;
@@ -15,5 +16,12 @@ export const DEFAULT_DATALABELS_STYLE: BaseTextStyle = {
export const DEFAULT_HALO_OPTIONS: Required = {
enabled: true,
opacity: 0.25,
- size: 10,
+ size: 6,
+};
+
+export const DEFAULT_POINT_MARKER_OPTIONS: Omit, 'enabled'> = {
+ radius: 4,
+ borderColor: '',
+ borderWidth: 0,
+ symbol: 'circle',
};
diff --git a/src/plugins/d3/renderer/hooks/useSeries/prepare-area.ts b/src/plugins/d3/renderer/hooks/useSeries/prepare-area.ts
index 51bb660b..6db51cf9 100644
--- a/src/plugins/d3/renderer/hooks/useSeries/prepare-area.ts
+++ b/src/plugins/d3/renderer/hooks/useSeries/prepare-area.ts
@@ -9,18 +9,17 @@ import {
DEFAULT_DATALABELS_PADDING,
DEFAULT_DATALABELS_STYLE,
DEFAULT_HALO_OPTIONS,
+ DEFAULT_POINT_MARKER_OPTIONS,
} from './constants';
import {getRandomCKId} from '../../../../../utils';
import {getSeriesStackId, prepareLegendSymbol} from './utils';
+import {PointMarkerOptions} from '../../../../../types/widget-data/marker';
export const DEFAULT_LINE_WIDTH = 1;
export const DEFAULT_MARKER = {
+ ...DEFAULT_POINT_MARKER_OPTIONS,
enabled: false,
- symbol: 'circle',
- radius: 4,
- borderWidth: 0,
- borderColor: '',
};
type PrepareAreaSeriesArgs = {
@@ -32,7 +31,7 @@ type PrepareAreaSeriesArgs = {
function prepareMarker(series: AreaSeries, seriesOptions?: ChartKitWidgetSeriesOptions) {
const seriesHoverState = get(seriesOptions, 'area.states.hover');
- const markerNormalState = Object.assign(
+ const markerNormalState: Required = Object.assign(
{},
DEFAULT_MARKER,
seriesOptions?.area?.marker,
diff --git a/src/plugins/d3/renderer/hooks/useSeries/prepare-line-series.ts b/src/plugins/d3/renderer/hooks/useSeries/prepare-line.ts
similarity index 98%
rename from src/plugins/d3/renderer/hooks/useSeries/prepare-line-series.ts
rename to src/plugins/d3/renderer/hooks/useSeries/prepare-line.ts
index 6211cbf2..34cc1fc7 100644
--- a/src/plugins/d3/renderer/hooks/useSeries/prepare-line-series.ts
+++ b/src/plugins/d3/renderer/hooks/useSeries/prepare-line.ts
@@ -17,6 +17,7 @@ import {
DEFAULT_DATALABELS_STYLE,
DEFAULT_HALO_OPTIONS,
DEFAULT_LEGEND_SYMBOL_PADDING,
+ DEFAULT_POINT_MARKER_OPTIONS,
} from './constants';
import {getRandomCKId} from '../../../../../utils';
@@ -25,11 +26,8 @@ export const DEFAULT_LINE_WIDTH = 1;
export const DEFAULT_DASH_STYLE = DashStyle.Solid;
export const DEFAULT_MARKER = {
+ ...DEFAULT_POINT_MARKER_OPTIONS,
enabled: false,
- symbol: 'circle',
- radius: 4,
- borderWidth: 0,
- borderColor: '',
};
type PrepareLineSeriesArgs = {
diff --git a/src/plugins/d3/renderer/hooks/useSeries/prepare-scatter.ts b/src/plugins/d3/renderer/hooks/useSeries/prepare-scatter.ts
new file mode 100644
index 00000000..ee809a85
--- /dev/null
+++ b/src/plugins/d3/renderer/hooks/useSeries/prepare-scatter.ts
@@ -0,0 +1,73 @@
+import {ScaleOrdinal} from 'd3';
+import get from 'lodash/get';
+import merge from 'lodash/merge';
+import type {PreparedLegend, PreparedScatterSeries} from './types';
+import type {ChartKitWidgetSeriesOptions, ScatterSeries} from '../../../../../types';
+import {getSymbolType} from '../../utils';
+
+import {prepareLegendSymbol} from './utils';
+import {DEFAULT_HALO_OPTIONS, DEFAULT_POINT_MARKER_OPTIONS} from './constants';
+
+import {PointMarkerOptions} from '../../../../../types/widget-data/marker';
+import {getRandomCKId} from '../../../../../utils';
+
+function prepareMarker(
+ series: ScatterSeries,
+ seriesOptions: ChartKitWidgetSeriesOptions | undefined,
+ index: number,
+) {
+ const seriesHoverState = get(seriesOptions, 'scatter.states.hover');
+ const markerNormalState: Required = {
+ ...DEFAULT_POINT_MARKER_OPTIONS,
+ enabled: true,
+ symbol: (series as ScatterSeries).symbolType || getSymbolType(index),
+ };
+
+ const hoveredMarkerDefaultOptions = {
+ enabled: true,
+ radius: markerNormalState.radius,
+ borderWidth: 1,
+ borderColor: '#ffffff',
+ halo: DEFAULT_HALO_OPTIONS,
+ };
+
+ return {
+ states: {
+ normal: markerNormalState,
+ hover: merge(hoveredMarkerDefaultOptions, seriesHoverState?.marker),
+ },
+ };
+}
+
+interface PrepareScatterSeriesArgs {
+ colorScale: ScaleOrdinal;
+ series: ScatterSeries[];
+ legend: PreparedLegend;
+ seriesOptions?: ChartKitWidgetSeriesOptions;
+}
+
+export function prepareScatterSeries(args: PrepareScatterSeriesArgs): PreparedScatterSeries[] {
+ const {colorScale, series, seriesOptions, legend} = args;
+
+ return series.map((s, index) => {
+ const id = getRandomCKId();
+ const name = 'name' in s && s.name ? s.name : '';
+ const symbolType = (s as ScatterSeries).symbolType || getSymbolType(index);
+
+ const prepared: PreparedScatterSeries = {
+ id,
+ type: s.type,
+ name,
+ color: get(s, 'color', colorScale(name)),
+ visible: get(s, 'visible', true),
+ legend: {
+ enabled: get(s, 'legend.enabled', legend.enabled),
+ symbol: prepareLegendSymbol(s, symbolType),
+ },
+ data: s.data,
+ marker: prepareMarker(s, seriesOptions, index),
+ };
+
+ return prepared;
+ }, []);
+}
diff --git a/src/plugins/d3/renderer/hooks/useSeries/prepareSeries.ts b/src/plugins/d3/renderer/hooks/useSeries/prepareSeries.ts
index 781b613f..87b26ce4 100644
--- a/src/plugins/d3/renderer/hooks/useSeries/prepareSeries.ts
+++ b/src/plugins/d3/renderer/hooks/useSeries/prepareSeries.ts
@@ -1,5 +1,3 @@
-import cloneDeep from 'lodash/cloneDeep';
-import get from 'lodash/get';
import type {ScaleOrdinal} from 'd3';
import type {
@@ -10,46 +8,17 @@ import type {
ChartKitWidgetSeriesOptions,
LineSeries,
PieSeries,
+ ScatterSeries,
} from '../../../../../types';
-import {SymbolType} from '../../../../../constants';
-import {getSymbolType} from '../../utils';
-import {ScatterSeries} from '../../../../../types/widget-data';
-
-import type {PreparedLegend, PreparedSeries, PreparedScatterSeries} from './types';
-import {prepareLineSeries} from './prepare-line-series';
+import type {PreparedLegend, PreparedSeries} from './types';
+import {prepareLineSeries} from './prepare-line';
import {prepareBarXSeries} from './prepare-bar-x';
import {prepareBarYSeries} from './prepare-bar-y';
-import {prepareLegendSymbol} from './utils';
import {ChartKitError} from '../../../../../libs';
import {preparePieSeries} from './prepare-pie';
import {prepareArea} from './prepare-area';
-
-type PrepareAxisRelatedSeriesArgs = {
- colorScale: ScaleOrdinal;
- series: ChartKitWidgetSeries;
- legend: PreparedLegend;
- index: number;
-};
-
-function prepareAxisRelatedSeries(args: PrepareAxisRelatedSeriesArgs): PreparedScatterSeries[] {
- const {colorScale, series, legend, index} = args;
- const preparedSeries = cloneDeep(series) as PreparedScatterSeries;
- const name = 'name' in series && series.name ? series.name : '';
-
- const symbolType = ((series as ScatterSeries).symbolType || getSymbolType(index)) as SymbolType;
-
- preparedSeries.symbolType = symbolType;
- preparedSeries.color = 'color' in series && series.color ? series.color : colorScale(name);
- preparedSeries.name = name;
- preparedSeries.visible = get(preparedSeries, 'visible', true);
- preparedSeries.legend = {
- enabled: get(preparedSeries, 'legend.enabled', legend.enabled),
- symbol: prepareLegendSymbol(series, symbolType),
- };
-
- return [preparedSeries];
-}
+import {prepareScatterSeries} from './prepare-scatter';
export function prepareSeries(args: {
type: ChartKitWidgetSeries['type'];
@@ -76,12 +45,7 @@ export function prepareSeries(args: {
return prepareBarYSeries({series: series as BarYSeries[], legend, colorScale});
}
case 'scatter': {
- return series.reduce((acc, singleSeries, index) => {
- acc.push(
- ...prepareAxisRelatedSeries({series: singleSeries, legend, colorScale, index}),
- );
- return acc;
- }, []);
+ return prepareScatterSeries({series: series as ScatterSeries[], legend, colorScale});
}
case 'line': {
return prepareLineSeries({
diff --git a/src/plugins/d3/renderer/hooks/useSeries/types.ts b/src/plugins/d3/renderer/hooks/useSeries/types.ts
index 94a35d8e..3500f467 100644
--- a/src/plugins/d3/renderer/hooks/useSeries/types.ts
+++ b/src/plugins/d3/renderer/hooks/useSeries/types.ts
@@ -33,7 +33,7 @@ export type PathLegendSymbol = {
export type SymbolLegendSymbol = {
shape: 'symbol';
- symbolType: SymbolType;
+ symbolType: `${SymbolType}`;
} & Required;
export type PreparedLegendSymbol = RectLegendSymbol | PathLegendSymbol | SymbolLegendSymbol;
@@ -85,7 +85,24 @@ type BasePreparedSeries = {
export type PreparedScatterSeries = {
type: ScatterSeries['type'];
data: ScatterSeriesData[];
- symbolType: SymbolType;
+ marker: {
+ states: {
+ normal: {
+ symbol: `${SymbolType}`;
+ enabled: boolean;
+ radius: number;
+ borderWidth: number;
+ borderColor: string;
+ };
+ hover: {
+ enabled: boolean;
+ radius: number;
+ borderWidth: number;
+ borderColor: string;
+ halo: PreparedHaloOptions;
+ };
+ };
+ };
} & BasePreparedSeries;
export type PreparedBarXSeries = {
@@ -159,7 +176,7 @@ export type PreparedLineSeries = {
marker: {
states: {
normal: {
- symbol: string;
+ symbol: `${SymbolType}`;
enabled: boolean;
radius: number;
borderWidth: number;
@@ -194,7 +211,7 @@ export type PreparedAreaSeries = {
marker: {
states: {
normal: {
- symbol: string;
+ symbol: `${SymbolType}`;
enabled: boolean;
radius: number;
borderWidth: number;
diff --git a/src/plugins/d3/renderer/hooks/useSeries/utils.ts b/src/plugins/d3/renderer/hooks/useSeries/utils.ts
index d962176b..7360ff0f 100644
--- a/src/plugins/d3/renderer/hooks/useSeries/utils.ts
+++ b/src/plugins/d3/renderer/hooks/useSeries/utils.ts
@@ -21,7 +21,7 @@ export const getAllLegendItems = (series: PreparedSeries[]) => {
export function prepareLegendSymbol(
series: ChartKitWidgetSeries,
- symbolType?: SymbolType,
+ symbolType?: `${SymbolType}`,
): PreparedLegendSymbol {
const symbolOptions = series.legend?.symbol || {};
diff --git a/src/plugins/d3/renderer/hooks/useShapes/index.tsx b/src/plugins/d3/renderer/hooks/useShapes/index.tsx
index 77f2914c..ee9204b0 100644
--- a/src/plugins/d3/renderer/hooks/useShapes/index.tsx
+++ b/src/plugins/d3/renderer/hooks/useShapes/index.tsx
@@ -17,7 +17,7 @@ import type {
import {BarXSeriesShapes, prepareBarXData} from './bar-x';
import type {PreparedBarXData} from './bar-x';
import {ScatterSeriesShape, prepareScatterData} from './scatter';
-import type {PreparedScatterData} from './scatter';
+import type {PreparedScatterData} from './scatter/types';
import {PieSeriesShapes} from './pie';
import {preparePieData} from './pie/prepare-data';
import type {PreparedPieData} from './pie/types';
@@ -27,7 +27,7 @@ import type {PreparedLineData} from './line/types';
import {BarYSeriesShapes, prepareBarYData} from './bar-y';
import type {PreparedBarYData} from './bar-y/types';
export type {PreparedBarXData} from './bar-x';
-export type {PreparedScatterData} from './scatter';
+export type {PreparedScatterData} from './scatter/types';
import {AreaSeriesShapes} from './area';
import {prepareAreaData} from './area/prepare-data';
import type {PreparedAreaData} from './area/types';
diff --git a/src/plugins/d3/renderer/hooks/useShapes/marker.ts b/src/plugins/d3/renderer/hooks/useShapes/marker.ts
index 15c6ae8f..b09c06cc 100644
--- a/src/plugins/d3/renderer/hooks/useShapes/marker.ts
+++ b/src/plugins/d3/renderer/hooks/useShapes/marker.ts
@@ -1,13 +1,17 @@
-import {BaseType, Selection, symbol, symbolCircle, symbolSquare} from 'd3';
+import {BaseType, Selection, symbol} from 'd3';
import {MarkerData as LineMarkerData} from './line/types';
import {MarkerData as AreaMarkerData} from './area/types';
+import {MarkerData as ScatterMarkerData} from './scatter/types';
import {block} from '../../../../../utils/cn';
+import {SymbolType} from '../../../../../constants';
+import {getSymbol} from '../../utils';
+import get from 'lodash/get';
const b = block('d3-marker');
const haloClassName = b('halo');
const symbolClassName = b('symbol');
-type MarkerData = LineMarkerData | AreaMarkerData;
+type MarkerData = LineMarkerData | AreaMarkerData | ScatterMarkerData;
export function renderMarker(
selection: Selection,
@@ -22,9 +26,11 @@ export function renderMarker(
.append('path')
.attr('class', haloClassName)
.attr('d', (d) => {
- const type = d.point.series.marker.states.normal.symbol;
- const radius = d.point.series.marker.states.hover.halo.size;
- return getMarkerSymbol(type, radius);
+ const series = d.point.series;
+ const type = series.marker.states.normal.symbol;
+ const radius = get(d.point.data, 'radius', series.marker.states.hover.radius);
+ const haloSize = series.marker.states.hover.halo.size;
+ return getMarkerSymbol(type, radius + haloSize);
})
.attr('fill', (d) => d.point.series.color)
.attr('opacity', (d) => d.point.series.marker.states.hover.halo.opacity)
@@ -57,27 +63,21 @@ export function setMarker(
) {
selection
.attr('d', (d) => {
- const radius =
- d.point.series.marker.states[state].radius +
- d.point.series.marker.states[state].borderWidth;
- return getMarkerSymbol(d.point.series.marker.states.normal.symbol, radius);
+ const series = d.point.series;
+ const type = series.marker.states.normal.symbol;
+ const radius = get(d.point.data, 'radius', series.marker.states[state].radius);
+ const size = radius + series.marker.states[state].borderWidth;
+ return getMarkerSymbol(type, size);
})
.attr('stroke-width', (d) => d.point.series.marker.states[state].borderWidth)
.attr('stroke', (d) => d.point.series.marker.states[state].borderColor);
}
-export function getMarkerSymbol(type: string, radius: number) {
- switch (type) {
- case 'square': {
- const size = Math.pow(radius, 2) * Math.PI;
- return symbol(symbolSquare, size)();
- }
- case 'circle':
- default: {
- const size = Math.pow(radius, 2) * Math.PI;
- return symbol(symbolCircle, size)();
- }
- }
+export function getMarkerSymbol(type: `${SymbolType}` = SymbolType.Circle, radius: number) {
+ const symbolFn = getSymbol(type);
+ const size = Math.pow(radius, 2) * Math.PI;
+
+ return symbol(symbolFn, size)();
}
export function selectMarkerHalo(parentSelection: Selection) {
diff --git a/src/plugins/d3/renderer/hooks/useShapes/scatter/index.tsx b/src/plugins/d3/renderer/hooks/useShapes/scatter/index.tsx
index 9ff7b563..6f03ad93 100644
--- a/src/plugins/d3/renderer/hooks/useShapes/scatter/index.tsx
+++ b/src/plugins/d3/renderer/hooks/useShapes/scatter/index.tsx
@@ -1,19 +1,21 @@
import React from 'react';
import get from 'lodash/get';
-import {symbol, color, pointer, select} from 'd3';
-import type {BaseType, Dispatch, Selection} from 'd3';
+import {pointer, select} from 'd3';
+import type {BaseType, Dispatch} from 'd3';
import {block} from '../../../../../../utils/cn';
-
-import {extractD3DataFromNode, isNodeContainsD3Data, getSymbol} from '../../../utils';
-import type {NodeWithD3Data} from '../../../utils';
+import {TooltipDataChunkScatter} from '../../../../../../types';
import {PreparedSeriesOptions} from '../../useSeries/types';
-import type {PreparedScatterData} from './prepare-data';
-import {shapeKey} from '../utils';
-import {SymbolType} from '../../../../../../constants';
-
+import {setActiveState, shapeKey} from '../utils';
+import type {PreparedScatterData, MarkerData} from './types';
+import {
+ getMarkerHaloVisibility,
+ renderMarker,
+ selectMarkerHalo,
+ selectMarkerSymbol,
+ setMarker,
+} from '../marker';
export {prepareScatterData} from './prepare-data';
-export type {PreparedScatterData} from './prepare-data';
type ScatterSeriesShapeProps = {
dispatcher: Dispatch