From 4cd4bd23bf8184323a44b50d1ae0c824970de788 Mon Sep 17 00:00:00 2001 From: Artem Panchuk Date: Wed, 20 Dec 2023 17:29:05 +0300 Subject: [PATCH 01/10] Add shapes support for lines in D3 --- .../d3/__stories__/line/Shapes.stories.tsx | 187 ++++++++++++++++++ .../hooks/useSeries/prepare-line-series.ts | 4 + .../d3/renderer/hooks/useSeries/types.ts | 2 + .../renderer/hooks/useShapes/line/index.tsx | 25 ++- .../hooks/useShapes/line/prepare-data.ts | 10 +- .../d3/renderer/hooks/useShapes/line/types.ts | 3 +- src/types/widget-data/line.ts | 2 + src/types/widget-data/series.ts | 16 ++ 8 files changed, 243 insertions(+), 6 deletions(-) create mode 100644 src/plugins/d3/__stories__/line/Shapes.stories.tsx diff --git a/src/plugins/d3/__stories__/line/Shapes.stories.tsx b/src/plugins/d3/__stories__/line/Shapes.stories.tsx new file mode 100644 index 00000000..9a13ccc9 --- /dev/null +++ b/src/plugins/d3/__stories__/line/Shapes.stories.tsx @@ -0,0 +1,187 @@ +import React from 'react'; +import {StoryObj} from '@storybook/react'; +import {Button} from '@gravity-ui/uikit'; +import {settings} from '../../../../libs'; +import {D3Plugin} from '../..'; +import {ChartKitWidgetData, LineSeriesData} from '../../../../types'; +import {ChartKit} from '../../../../components/ChartKit'; +import nintendoGames from '../../examples/nintendoGames'; +import {HighchartsPlugin} from '../../../highcharts'; + +enum LineShapeType { + Solid = 'Solid', + ShortDash = 'ShortDash', + ShortDot = 'ShortDot', + ShortDashDot = 'ShortDashDot', + ShortDashDotDot = 'ShortDashDotDot', + Dot = 'Dot', + Dash = 'Dash', + LongDash = 'LongDash', + DashDot = 'DashDot', + LongDashDot = 'LongDashDot', + LongDashDotDot = 'LongDashDotDot', +} +const SHAPES_ORDER = { + [LineShapeType.Solid]: 1, + [LineShapeType.Dash]: 2, + [LineShapeType.Dot]: 3, + [LineShapeType.ShortDashDot]: 4, + [LineShapeType.LongDash]: 5, + [LineShapeType.LongDashDot]: 6, + [LineShapeType.ShortDot]: 7, + [LineShapeType.LongDashDotDot]: 8, + [LineShapeType.ShortDash]: 9, + [LineShapeType.DashDot]: 10, + [LineShapeType.ShortDashDotDot]: 11, +}; + +const selectShapes = (): LineShapeType[] => Object.values(LineShapeType); +const getServerShapesOrder = () => selectShapes().sort((a, b) => SHAPES_ORDER[a] - SHAPES_ORDER[b]); + +const SHAPES_IN_ORDER = getServerShapesOrder(); + +function prepareData(): ChartKitWidgetData { + const games = nintendoGames.filter((d) => { + return d.date && d.user_score; + }); + + const byGenre = (genre: string) => { + return games + .filter((d) => d.genres.includes(genre)) + .map((d) => { + return { + x: d.date, + y: d.user_score, + label: d.title, + }; + }) as LineSeriesData[]; + }; + + return { + series: { + options: { + line: { + lineWidth: 2, + }, + }, + data: [ + { + name: '3D', + type: 'line', + data: byGenre('3D'), + dataLabels: { + enabled: true, + }, + }, + { + name: '2D', + type: 'line', + data: byGenre('2D'), + dataLabels: { + enabled: true, + }, + }, + { + name: 'Strategy', + type: 'line', + data: byGenre('Strategy'), + dataLabels: { + enabled: true, + }, + }, + { + name: 'Shooter', + type: 'line', + data: byGenre('Shooter'), + dataLabels: { + enabled: true, + }, + }, + ], + }, + xAxis: { + type: 'datetime', + title: { + text: 'Release date', + }, + }, + yAxis: [ + { + title: {text: 'User score'}, + labels: { + enabled: true, + }, + ticks: { + pixelInterval: 120, + }, + }, + ], + }; +} + +const ChartStory = ({data}: {data: ChartKitWidgetData}) => { + const [shown, setShown] = React.useState(false); + + if (!shown) { + settings.set({plugins: [D3Plugin, HighchartsPlugin]}); + return ; + } + + data.series.data.forEach((graph, i) => { + // @ts-ignore + graph.dashStyle = SHAPES_IN_ORDER[i % SHAPES_IN_ORDER.length]; + }); + + return ( + <> +
+ +
+
+ +
+ + ); +}; + +export const ShapesLineChartStory: StoryObj = { + name: 'Shapes', + args: { + data: prepareData(), + }, + argTypes: { + data: { + control: 'object', + }, + }, +}; + +export default { + title: 'Plugins/D3/Line', + component: ChartStory, +}; diff --git a/src/plugins/d3/renderer/hooks/useSeries/prepare-line-series.ts b/src/plugins/d3/renderer/hooks/useSeries/prepare-line-series.ts index 5e7ac7cb..66003546 100644 --- a/src/plugins/d3/renderer/hooks/useSeries/prepare-line-series.ts +++ b/src/plugins/d3/renderer/hooks/useSeries/prepare-line-series.ts @@ -108,6 +108,10 @@ export function prepareLineSeries(args: PrepareLineSeriesArgs) { marker: prepareMarker(series, seriesOptions), }; + if (series.dashStyle) { + prepared.dashStyle = series.dashStyle; + } + return prepared; }, []); } diff --git a/src/plugins/d3/renderer/hooks/useSeries/types.ts b/src/plugins/d3/renderer/hooks/useSeries/types.ts index 60239049..d14a92fb 100644 --- a/src/plugins/d3/renderer/hooks/useSeries/types.ts +++ b/src/plugins/d3/renderer/hooks/useSeries/types.ts @@ -15,6 +15,7 @@ import { ConnectorShape, ConnectorCurve, PathLegendSymbolOptions, + DashStyle, } from '../../../../../types'; import type {SeriesOptionsDefaults} from '../../constants'; @@ -153,6 +154,7 @@ export type PreparedLineSeries = { }; }; }; + dashStyle?: DashStyle; } & BasePreparedSeries; export type PreparedSeries = diff --git a/src/plugins/d3/renderer/hooks/useShapes/line/index.tsx b/src/plugins/d3/renderer/hooks/useShapes/line/index.tsx index 053397ea..62503463 100644 --- a/src/plugins/d3/renderer/hooks/useShapes/line/index.tsx +++ b/src/plugins/d3/renderer/hooks/useShapes/line/index.tsx @@ -6,7 +6,7 @@ import get from 'lodash/get'; import {block} from '../../../../../../utils/cn'; import type {PreparedSeriesOptions} from '../../useSeries/types'; import type {MarkerData, PointData, PreparedLineData} from './types'; -import type {TooltipDataChunkLine} from '../../../../../../types'; +import type {DashStyle, TooltipDataChunkLine} from '../../../../../../types'; import type {LabelData} from '../../../types'; import {filterOverlappingLabels} from '../../../utils'; import {setActiveState} from '../utils'; @@ -48,6 +48,26 @@ function getMarkerSymbol(type: string, radius: number) { } } +const getLineDashArray = (dashStyle: DashStyle = 'Solid', strokeWidth = 2) => { + const value = dashStyle && dashStyle.toLowerCase(); + + const arrayValue = value + .replace('shortdashdotdot', '3,1,1,1,1,1,') + .replace('shortdashdot', '3,1,1,1') + .replace('shortdot', '1,1,') + .replace('shortdash', '3,1,') + .replace('longdash', '8,3,') + .replace(/dot/g, '1,3,') + .replace('dash', '4,3,') + .replace(/,$/, '') + .split(',') + .map((part) => { + return `${parseInt(part, 10) * strokeWidth}`; + }); + + return arrayValue.join(',').replace(/NaN/g, 'none'); +}; + const getMarkerVisibility = (d: MarkerData) => { const markerStates = d.point.series.marker.states; const enabled = (markerStates.hover.enabled && d.hovered) || markerStates.normal.enabled; @@ -88,8 +108,7 @@ export const LineSeriesShapes = (args: Args) => { .attr('fill', 'none') .attr('stroke', (d) => d.color) .attr('stroke-width', (d) => d.width) - .attr('stroke-linejoin', 'round') - .attr('stroke-linecap', 'round'); + .attr('stroke-dasharray', (d) => getLineDashArray(d.dashStyle, d.width)); let dataLabels = preparedData.reduce((acc, d) => { return acc.concat(d.labels); diff --git a/src/plugins/d3/renderer/hooks/useShapes/line/prepare-data.ts b/src/plugins/d3/renderer/hooks/useShapes/line/prepare-data.ts index e42f2743..9c21e332 100644 --- a/src/plugins/d3/renderer/hooks/useShapes/line/prepare-data.ts +++ b/src/plugins/d3/renderer/hooks/useShapes/line/prepare-data.ts @@ -70,7 +70,7 @@ export const prepareLineData = (args: { })); } - acc.push({ + const result: PreparedLineData = { points, markers, labels, @@ -80,7 +80,13 @@ export const prepareLineData = (args: { hovered: false, active: true, id: s.id, - }); + }; + + if (s.dashStyle) { + result.dashStyle = s.dashStyle; + } + + acc.push(result); return acc; }, []); diff --git a/src/plugins/d3/renderer/hooks/useShapes/line/types.ts b/src/plugins/d3/renderer/hooks/useShapes/line/types.ts index 5321563e..9397d1fc 100644 --- a/src/plugins/d3/renderer/hooks/useShapes/line/types.ts +++ b/src/plugins/d3/renderer/hooks/useShapes/line/types.ts @@ -1,5 +1,5 @@ import {PreparedLineSeries} from '../../useSeries/types'; -import {LineSeriesData} from '../../../../../../types'; +import {DashStyle, LineSeriesData} from '../../../../../../types'; import {LabelData} from '../../../types'; export type PointData = { @@ -25,4 +25,5 @@ export type PreparedLineData = { hovered: boolean; active: boolean; labels: LabelData[]; + dashStyle?: DashStyle; }; diff --git a/src/types/widget-data/line.ts b/src/types/widget-data/line.ts index c1dc9681..ff1c9724 100644 --- a/src/types/widget-data/line.ts +++ b/src/types/widget-data/line.ts @@ -1,6 +1,7 @@ import {BaseSeries, BaseSeriesData} from './base'; import {ChartKitWidgetLegend, RectLegendSymbolOptions} from './legend'; import {PointMarkerOptions} from './marker'; +import {DashStyle} from './series'; export type LineSeriesData = BaseSeriesData & { /** @@ -45,4 +46,5 @@ export type LineSeries = BaseSeries & { }; /** Options for the point markers of line series */ marker?: LineMarkerOptions; + dashStyle?: DashStyle; }; diff --git a/src/types/widget-data/series.ts b/src/types/widget-data/series.ts index 341bbaa6..ee7ea83c 100644 --- a/src/types/widget-data/series.ts +++ b/src/types/widget-data/series.ts @@ -24,6 +24,19 @@ export type DataLabelRendererData = { data: ChartKitWidgetSeriesData; }; +export type DashStyle = + | 'Dash' + | 'DashDot' + | 'Dot' + | 'LongDash' + | 'LongDashDot' + | 'LongDashDotDot' + | 'ShortDash' + | 'ShortDashDot' + | 'ShortDashDotDot' + | 'ShortDot' + | 'Solid'; + type BasicHoverState = { /** * Enable separate styles for the hovered series. @@ -174,5 +187,8 @@ export type ChartKitWidgetSeriesOptions = { }; /** Options for the point markers of line series */ marker?: LineMarkerOptions; + + /** Options for line style */ + dashStyle?: DashStyle; }; }; From 081fade06ef634478ac2087c501bcd42c0535e52 Mon Sep 17 00:00:00 2001 From: Artem Panchuk Date: Mon, 25 Dec 2023 12:37:01 +0300 Subject: [PATCH 02/10] Fix dashStyle type --- .../hooks/useSeries/prepare-line-series.ts | 10 +++--- .../d3/renderer/hooks/useSeries/types.ts | 2 +- .../renderer/hooks/useShapes/line/index.tsx | 4 +-- .../d3/renderer/hooks/useShapes/line/types.ts | 2 +- src/types/widget-data/series.ts | 36 +++++++++++-------- 5 files changed, 31 insertions(+), 23 deletions(-) diff --git a/src/plugins/d3/renderer/hooks/useSeries/prepare-line-series.ts b/src/plugins/d3/renderer/hooks/useSeries/prepare-line-series.ts index 27f9b772..f7ab69fb 100644 --- a/src/plugins/d3/renderer/hooks/useSeries/prepare-line-series.ts +++ b/src/plugins/d3/renderer/hooks/useSeries/prepare-line-series.ts @@ -5,6 +5,7 @@ import merge from 'lodash/merge'; import { ChartKitWidgetSeries, ChartKitWidgetSeriesOptions, + DashStyle, LineSeries, RectLegendSymbolOptions, } from '../../../../../types'; @@ -36,6 +37,10 @@ type PrepareLineSeriesArgs = { legend: PreparedLegend; }; +function prepareDashStyle(series: LineSeries) { + return series.dashStyle || DashStyle.Solid; +} + function prepareLineLegendSymbol( series: ChartKitWidgetSeries, seriesOptions?: ChartKitWidgetSeriesOptions, @@ -103,12 +108,9 @@ export function prepareLineSeries(args: PrepareLineSeriesArgs) { allowOverlap: get(series, 'dataLabels.allowOverlap', false), }, marker: prepareMarker(series, seriesOptions), + dashStyle: prepareDashStyle(series), }; - if (series.dashStyle) { - prepared.dashStyle = series.dashStyle; - } - return prepared; }, []); } diff --git a/src/plugins/d3/renderer/hooks/useSeries/types.ts b/src/plugins/d3/renderer/hooks/useSeries/types.ts index 9a24cd0b..0eabdaf0 100644 --- a/src/plugins/d3/renderer/hooks/useSeries/types.ts +++ b/src/plugins/d3/renderer/hooks/useSeries/types.ts @@ -156,7 +156,7 @@ export type PreparedLineSeries = { }; }; }; - dashStyle?: DashStyle; + dashStyle: DashStyle; } & BasePreparedSeries; export type PreparedAreaSeries = { diff --git a/src/plugins/d3/renderer/hooks/useShapes/line/index.tsx b/src/plugins/d3/renderer/hooks/useShapes/line/index.tsx index 62503463..53697e36 100644 --- a/src/plugins/d3/renderer/hooks/useShapes/line/index.tsx +++ b/src/plugins/d3/renderer/hooks/useShapes/line/index.tsx @@ -48,8 +48,8 @@ function getMarkerSymbol(type: string, radius: number) { } } -const getLineDashArray = (dashStyle: DashStyle = 'Solid', strokeWidth = 2) => { - const value = dashStyle && dashStyle.toLowerCase(); +const getLineDashArray = (dashStyle: DashStyle, strokeWidth = 2) => { + const value = dashStyle.toLowerCase(); const arrayValue = value .replace('shortdashdotdot', '3,1,1,1,1,1,') diff --git a/src/plugins/d3/renderer/hooks/useShapes/line/types.ts b/src/plugins/d3/renderer/hooks/useShapes/line/types.ts index 9397d1fc..5a79d10a 100644 --- a/src/plugins/d3/renderer/hooks/useShapes/line/types.ts +++ b/src/plugins/d3/renderer/hooks/useShapes/line/types.ts @@ -25,5 +25,5 @@ export type PreparedLineData = { hovered: boolean; active: boolean; labels: LabelData[]; - dashStyle?: DashStyle; + dashStyle: DashStyle; }; diff --git a/src/types/widget-data/series.ts b/src/types/widget-data/series.ts index 32981c19..95ecfc00 100644 --- a/src/types/widget-data/series.ts +++ b/src/types/widget-data/series.ts @@ -27,18 +27,21 @@ export type DataLabelRendererData = { data: ChartKitWidgetSeriesData; }; -export type DashStyle = - | 'Dash' - | 'DashDot' - | 'Dot' - | 'LongDash' - | 'LongDashDot' - | 'LongDashDotDot' - | 'ShortDash' - | 'ShortDashDot' - | 'ShortDashDotDot' - | 'ShortDot' - | 'Solid'; +export enum DashStyle { + Dash = 'Dash', + DashDot = 'DashDot', + Dot = 'Dot', + LongDash = 'LongDash', + LongDashDot = 'LongDashDot', + LongDashDotDot = 'LongDashDotDot', + ShortDash = 'ShortDash', + ShortDashDot = 'ShortDashDot', + ShortDashDotDot = 'ShortDashDotDot', + ShortDot = 'ShortDot', + Solid = 'Solid', +} + +export enum LineCap {} type BasicHoverState = { /** @@ -186,6 +189,12 @@ export type ChartKitWidgetSeriesOptions = { }; /** Options for the point markers of line series */ marker?: LineMarkerOptions; + + /** Options for line style + * + * @default 'Solid' + * */ + dashStyle?: DashStyle; }; area?: { /** Pixel width of the graph line. @@ -205,8 +214,5 @@ export type ChartKitWidgetSeriesOptions = { }; /** Options for the point markers of line series */ marker?: LineMarkerOptions; - - /** Options for line style */ - dashStyle?: DashStyle; }; }; From e59e7db31c4c32e0d6539c14b8ff19ff3a6600dc Mon Sep 17 00:00:00 2001 From: Artem Panchuk Date: Mon, 25 Dec 2023 15:40:22 +0300 Subject: [PATCH 03/10] Add linecap option, fix story --- .../d3/__stories__/line/Shapes.stories.tsx | 48 +++++++------------ .../hooks/useSeries/prepare-line-series.ts | 6 +++ .../d3/renderer/hooks/useSeries/types.ts | 2 + .../renderer/hooks/useShapes/line/index.tsx | 2 + .../hooks/useShapes/line/prepare-data.ts | 6 +-- .../d3/renderer/hooks/useShapes/line/types.ts | 3 +- src/types/widget-data/line.ts | 3 +- src/types/widget-data/series.ts | 13 ++++- 8 files changed, 45 insertions(+), 38 deletions(-) diff --git a/src/plugins/d3/__stories__/line/Shapes.stories.tsx b/src/plugins/d3/__stories__/line/Shapes.stories.tsx index 9a13ccc9..ae0f4055 100644 --- a/src/plugins/d3/__stories__/line/Shapes.stories.tsx +++ b/src/plugins/d3/__stories__/line/Shapes.stories.tsx @@ -3,42 +3,29 @@ import {StoryObj} from '@storybook/react'; import {Button} from '@gravity-ui/uikit'; import {settings} from '../../../../libs'; import {D3Plugin} from '../..'; -import {ChartKitWidgetData, LineSeriesData} from '../../../../types'; +import {ChartKitWidgetData, LineSeriesData, LineSeries, DashStyle} from '../../../../types'; import {ChartKit} from '../../../../components/ChartKit'; import nintendoGames from '../../examples/nintendoGames'; import {HighchartsPlugin} from '../../../highcharts'; -enum LineShapeType { - Solid = 'Solid', - ShortDash = 'ShortDash', - ShortDot = 'ShortDot', - ShortDashDot = 'ShortDashDot', - ShortDashDotDot = 'ShortDashDotDot', - Dot = 'Dot', - Dash = 'Dash', - LongDash = 'LongDash', - DashDot = 'DashDot', - LongDashDot = 'LongDashDot', - LongDashDotDot = 'LongDashDotDot', -} -const SHAPES_ORDER = { - [LineShapeType.Solid]: 1, - [LineShapeType.Dash]: 2, - [LineShapeType.Dot]: 3, - [LineShapeType.ShortDashDot]: 4, - [LineShapeType.LongDash]: 5, - [LineShapeType.LongDashDot]: 6, - [LineShapeType.ShortDot]: 7, - [LineShapeType.LongDashDotDot]: 8, - [LineShapeType.ShortDash]: 9, - [LineShapeType.DashDot]: 10, - [LineShapeType.ShortDashDotDot]: 11, +const SHAPES = { + [DashStyle.Solid]: 1, + [DashStyle.Dash]: 2, + [DashStyle.Dot]: 3, + [DashStyle.ShortDashDot]: 4, + [DashStyle.LongDash]: 5, + [DashStyle.LongDashDot]: 6, + [DashStyle.ShortDot]: 7, + [DashStyle.LongDashDotDot]: 8, + [DashStyle.ShortDash]: 9, + [DashStyle.DashDot]: 10, + [DashStyle.ShortDashDotDot]: 11, }; -const selectShapes = (): LineShapeType[] => Object.values(LineShapeType); -const getServerShapesOrder = () => selectShapes().sort((a, b) => SHAPES_ORDER[a] - SHAPES_ORDER[b]); +const selectShapes = (): DashStyle[] => Object.values(DashStyle); +const getShapesOrder = () => selectShapes().sort((a, b) => SHAPES[a] - SHAPES[b]); -const SHAPES_IN_ORDER = getServerShapesOrder(); +const SHAPES_IN_ORDER = getShapesOrder(); function prepareData(): ChartKitWidgetData { const games = nintendoGames.filter((d) => { @@ -127,8 +114,7 @@ const ChartStory = ({data}: {data: ChartKitWidgetData}) => { return ; } - data.series.data.forEach((graph, i) => { - // @ts-ignore + (data.series.data as LineSeries[]).forEach((graph, i) => { graph.dashStyle = SHAPES_IN_ORDER[i % SHAPES_IN_ORDER.length]; }); diff --git a/src/plugins/d3/renderer/hooks/useSeries/prepare-line-series.ts b/src/plugins/d3/renderer/hooks/useSeries/prepare-line-series.ts index f7ab69fb..f5fa1516 100644 --- a/src/plugins/d3/renderer/hooks/useSeries/prepare-line-series.ts +++ b/src/plugins/d3/renderer/hooks/useSeries/prepare-line-series.ts @@ -6,6 +6,7 @@ import { ChartKitWidgetSeries, ChartKitWidgetSeriesOptions, DashStyle, + LineCap, LineSeries, RectLegendSymbolOptions, } from '../../../../../types'; @@ -41,6 +42,10 @@ function prepareDashStyle(series: LineSeries) { return series.dashStyle || DashStyle.Solid; } +function prepareLinecap(series: LineSeries) { + return series.linecap || LineCap.None; +} + function prepareLineLegendSymbol( series: ChartKitWidgetSeries, seriesOptions?: ChartKitWidgetSeriesOptions, @@ -109,6 +114,7 @@ export function prepareLineSeries(args: PrepareLineSeriesArgs) { }, marker: prepareMarker(series, seriesOptions), dashStyle: prepareDashStyle(series), + linecap: prepareLinecap(series), }; return prepared; diff --git a/src/plugins/d3/renderer/hooks/useSeries/types.ts b/src/plugins/d3/renderer/hooks/useSeries/types.ts index 0eabdaf0..83c9ad1f 100644 --- a/src/plugins/d3/renderer/hooks/useSeries/types.ts +++ b/src/plugins/d3/renderer/hooks/useSeries/types.ts @@ -16,6 +16,7 @@ import { ConnectorCurve, PathLegendSymbolOptions, DashStyle, + LineCap, AreaSeries, AreaSeriesData, } from '../../../../../types'; @@ -157,6 +158,7 @@ export type PreparedLineSeries = { }; }; dashStyle: DashStyle; + linecap: LineCap; } & BasePreparedSeries; export type PreparedAreaSeries = { diff --git a/src/plugins/d3/renderer/hooks/useShapes/line/index.tsx b/src/plugins/d3/renderer/hooks/useShapes/line/index.tsx index 53697e36..fa5b6a28 100644 --- a/src/plugins/d3/renderer/hooks/useShapes/line/index.tsx +++ b/src/plugins/d3/renderer/hooks/useShapes/line/index.tsx @@ -108,6 +108,8 @@ export const LineSeriesShapes = (args: Args) => { .attr('fill', 'none') .attr('stroke', (d) => d.color) .attr('stroke-width', (d) => d.width) + .attr('stroke-linejoin', (d) => d.linecap) + .attr('stroke-linecap', (d) => d.linecap) .attr('stroke-dasharray', (d) => getLineDashArray(d.dashStyle, d.width)); let dataLabels = preparedData.reduce((acc, d) => { diff --git a/src/plugins/d3/renderer/hooks/useShapes/line/prepare-data.ts b/src/plugins/d3/renderer/hooks/useShapes/line/prepare-data.ts index 9c21e332..6e323ed4 100644 --- a/src/plugins/d3/renderer/hooks/useShapes/line/prepare-data.ts +++ b/src/plugins/d3/renderer/hooks/useShapes/line/prepare-data.ts @@ -80,12 +80,10 @@ export const prepareLineData = (args: { hovered: false, active: true, id: s.id, + dashStyle: s.dashStyle, + linecap: s.linecap, }; - if (s.dashStyle) { - result.dashStyle = s.dashStyle; - } - acc.push(result); return acc; diff --git a/src/plugins/d3/renderer/hooks/useShapes/line/types.ts b/src/plugins/d3/renderer/hooks/useShapes/line/types.ts index 5a79d10a..dfc42c3d 100644 --- a/src/plugins/d3/renderer/hooks/useShapes/line/types.ts +++ b/src/plugins/d3/renderer/hooks/useShapes/line/types.ts @@ -1,5 +1,5 @@ import {PreparedLineSeries} from '../../useSeries/types'; -import {DashStyle, LineSeriesData} from '../../../../../../types'; +import {DashStyle, LineCap, LineSeriesData} from '../../../../../../types'; import {LabelData} from '../../../types'; export type PointData = { @@ -26,4 +26,5 @@ export type PreparedLineData = { active: boolean; labels: LabelData[]; dashStyle: DashStyle; + linecap: LineCap; }; diff --git a/src/types/widget-data/line.ts b/src/types/widget-data/line.ts index f1939203..cd40a5ab 100644 --- a/src/types/widget-data/line.ts +++ b/src/types/widget-data/line.ts @@ -1,7 +1,7 @@ import type {BaseSeries, BaseSeriesData} from './base'; import type {ChartKitWidgetLegend, RectLegendSymbolOptions} from './legend'; import type {PointMarkerOptions} from './marker'; -import type {DashStyle} from './series'; +import type {DashStyle, LineCap} from './series'; export type LineSeriesData = BaseSeriesData & { /** @@ -47,4 +47,5 @@ export type LineSeries = BaseSeries & { /** Options for the point markers of line series */ marker?: LineMarkerOptions; dashStyle?: DashStyle; + linecap?: LineCap; }; diff --git a/src/types/widget-data/series.ts b/src/types/widget-data/series.ts index 95ecfc00..99aa3bcf 100644 --- a/src/types/widget-data/series.ts +++ b/src/types/widget-data/series.ts @@ -41,7 +41,12 @@ export enum DashStyle { Solid = 'Solid', } -export enum LineCap {} +export enum LineCap { + Butt = 'butt', + Round = 'round', + Square = 'square', + None = 'none', +} type BasicHoverState = { /** @@ -195,6 +200,12 @@ export type ChartKitWidgetSeriesOptions = { * @default 'Solid' * */ dashStyle?: DashStyle; + + /** Options for line cap style + * + * @default 'round' + * */ + linecap?: LineCap; }; area?: { /** Pixel width of the graph line. From d68f2d0e436c39d36dbcb4d3c52a72da9f039da4 Mon Sep 17 00:00:00 2001 From: Artem Panchuk Date: Mon, 25 Dec 2023 18:40:55 +0300 Subject: [PATCH 04/10] Add legend dash style --- src/plugins/d3/renderer/components/Legend.tsx | 3 +++ .../d3/renderer/hooks/useSeries/types.ts | 1 + .../renderer/hooks/useShapes/line/index.tsx | 24 ++----------------- .../d3/renderer/hooks/useShapes/utils.ts | 22 ++++++++++++++++- 4 files changed, 27 insertions(+), 23 deletions(-) diff --git a/src/plugins/d3/renderer/components/Legend.tsx b/src/plugins/d3/renderer/components/Legend.tsx index 2a7c6d0f..a38751c0 100644 --- a/src/plugins/d3/renderer/components/Legend.tsx +++ b/src/plugins/d3/renderer/components/Legend.tsx @@ -11,6 +11,8 @@ import type { LegendConfig, } from '../hooks'; +import {getLineDashArray} from '../hooks/useShapes/utils'; + const b = block('d3-legend'); type Props = { @@ -136,6 +138,7 @@ function renderLegendSymbol(args: { .attr('d', legendSymbolGenerator(points)) .attr('fill', 'none') .attr('stroke-width', d.symbol.strokeWidth) + .attr('stroke-dasharray', getLineDashArray(d.dashStyle, d.symbol.strokeWidth)) .attr('class', className) .style('stroke', color); diff --git a/src/plugins/d3/renderer/hooks/useSeries/types.ts b/src/plugins/d3/renderer/hooks/useSeries/types.ts index 83c9ad1f..099567bb 100644 --- a/src/plugins/d3/renderer/hooks/useSeries/types.ts +++ b/src/plugins/d3/renderer/hooks/useSeries/types.ts @@ -46,6 +46,7 @@ export type LegendItem = { symbol: PreparedLegendSymbol; textWidth: number; visible?: boolean; + dashStyle: DashStyle; }; export type LegendConfig = { diff --git a/src/plugins/d3/renderer/hooks/useShapes/line/index.tsx b/src/plugins/d3/renderer/hooks/useShapes/line/index.tsx index fa5b6a28..2d01124c 100644 --- a/src/plugins/d3/renderer/hooks/useShapes/line/index.tsx +++ b/src/plugins/d3/renderer/hooks/useShapes/line/index.tsx @@ -6,10 +6,10 @@ import get from 'lodash/get'; import {block} from '../../../../../../utils/cn'; import type {PreparedSeriesOptions} from '../../useSeries/types'; import type {MarkerData, PointData, PreparedLineData} from './types'; -import type {DashStyle, TooltipDataChunkLine} from '../../../../../../types'; +import type {TooltipDataChunkLine} from '../../../../../../types'; import type {LabelData} from '../../../types'; import {filterOverlappingLabels} from '../../../utils'; -import {setActiveState} from '../utils'; +import {getLineDashArray, setActiveState} from '../utils'; const b = block('d3-line'); @@ -48,26 +48,6 @@ function getMarkerSymbol(type: string, radius: number) { } } -const getLineDashArray = (dashStyle: DashStyle, strokeWidth = 2) => { - const value = dashStyle.toLowerCase(); - - const arrayValue = value - .replace('shortdashdotdot', '3,1,1,1,1,1,') - .replace('shortdashdot', '3,1,1,1') - .replace('shortdot', '1,1,') - .replace('shortdash', '3,1,') - .replace('longdash', '8,3,') - .replace(/dot/g, '1,3,') - .replace('dash', '4,3,') - .replace(/,$/, '') - .split(',') - .map((part) => { - return `${parseInt(part, 10) * strokeWidth}`; - }); - - return arrayValue.join(',').replace(/NaN/g, 'none'); -}; - const getMarkerVisibility = (d: MarkerData) => { const markerStates = d.point.series.marker.states; const enabled = (markerStates.hover.enabled && d.hovered) || markerStates.normal.enabled; diff --git a/src/plugins/d3/renderer/hooks/useShapes/utils.ts b/src/plugins/d3/renderer/hooks/useShapes/utils.ts index 2cc52370..25f25985 100644 --- a/src/plugins/d3/renderer/hooks/useShapes/utils.ts +++ b/src/plugins/d3/renderer/hooks/useShapes/utils.ts @@ -2,7 +2,7 @@ import type {BaseType, ScaleBand, ScaleLinear, ScaleTime} from 'd3'; import {select} from 'd3'; import get from 'lodash/get'; -import type {BasicInactiveState} from '../../../../../types'; +import type {BasicInactiveState, DashStyle} from '../../../../../types'; import {getDataCategoryValue} from '../../utils'; import type {ChartScale} from '../useAxisScales'; import type {PreparedAxis} from '../useChartOptions/types'; @@ -64,3 +64,23 @@ export function setActiveState(args: { return datum; } + +export const getLineDashArray = (dashStyle: DashStyle, strokeWidth = 2) => { + const value = dashStyle.toLowerCase(); + + const arrayValue = value + .replace('shortdashdotdot', '3,1,1,1,1,1,') + .replace('shortdashdot', '3,1,1,1') + .replace('shortdot', '1,1,') + .replace('shortdash', '3,1,') + .replace('longdash', '8,3,') + .replace(/dot/g, '1,3,') + .replace('dash', '4,3,') + .replace(/,$/, '') + .split(',') + .map((part) => { + return `${parseInt(part, 10) * strokeWidth}`; + }); + + return arrayValue.join(',').replace(/NaN/g, 'none'); +}; From 4bab13c3c32afd69207395cd1795b84c77881964 Mon Sep 17 00:00:00 2001 From: Artem Panchuk Date: Tue, 26 Dec 2023 12:42:31 +0300 Subject: [PATCH 05/10] Fix legend types --- src/plugins/d3/renderer/components/Legend.tsx | 8 +++++++- src/plugins/d3/renderer/hooks/useSeries/types.ts | 2 +- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/src/plugins/d3/renderer/components/Legend.tsx b/src/plugins/d3/renderer/components/Legend.tsx index a38751c0..bc2db53f 100644 --- a/src/plugins/d3/renderer/components/Legend.tsx +++ b/src/plugins/d3/renderer/components/Legend.tsx @@ -138,10 +138,16 @@ function renderLegendSymbol(args: { .attr('d', legendSymbolGenerator(points)) .attr('fill', 'none') .attr('stroke-width', d.symbol.strokeWidth) - .attr('stroke-dasharray', getLineDashArray(d.dashStyle, d.symbol.strokeWidth)) .attr('class', className) .style('stroke', color); + if (d.dashStyle) { + element.attr( + 'stroke-dasharray', + getLineDashArray(d.dashStyle, d.symbol.strokeWidth), + ); + } + break; } case 'rect': { diff --git a/src/plugins/d3/renderer/hooks/useSeries/types.ts b/src/plugins/d3/renderer/hooks/useSeries/types.ts index 099567bb..7aa3be00 100644 --- a/src/plugins/d3/renderer/hooks/useSeries/types.ts +++ b/src/plugins/d3/renderer/hooks/useSeries/types.ts @@ -46,7 +46,7 @@ export type LegendItem = { symbol: PreparedLegendSymbol; textWidth: number; visible?: boolean; - dashStyle: DashStyle; + dashStyle?: DashStyle; }; export type LegendConfig = { From 4974d052b653946dafce98cde0af313d3c8ab258 Mon Sep 17 00:00:00 2001 From: Artem Panchuk Date: Tue, 26 Dec 2023 15:21:01 +0300 Subject: [PATCH 06/10] Fix story, default value and minor issues --- .../d3/__stories__/Showcase.stories.tsx | 7 + src/plugins/d3/examples/line/Shapes.tsx | 121 ++++++++++++++++++ .../hooks/useSeries/prepare-line-series.ts | 2 +- src/types/widget-data/line.ts | 2 + src/types/widget-data/series.ts | 3 +- 5 files changed, 133 insertions(+), 2 deletions(-) create mode 100644 src/plugins/d3/examples/line/Shapes.tsx diff --git a/src/plugins/d3/__stories__/Showcase.stories.tsx b/src/plugins/d3/__stories__/Showcase.stories.tsx index 84778a6c..016f0f5e 100644 --- a/src/plugins/d3/__stories__/Showcase.stories.tsx +++ b/src/plugins/d3/__stories__/Showcase.stories.tsx @@ -16,6 +16,7 @@ import {BasicPie} from '../examples/pie/Basic'; import {Basic as BasicScatter} from '../examples/scatter/Basic'; import {Basic as BasicLine} from '../examples/line/Basic'; import {Basic as BasicArea} from '../examples/area/Basic'; +import {LinesWithShapes} from '../examples/line/Shapes'; import {DataLabels as LineWithDataLabels} from '../examples/line/DataLabels'; import {Donut} from '../examples/pie/Donut'; import {LineAndBarXCombinedChart} from '../examples/combined/LineAndBarX'; @@ -52,6 +53,12 @@ const ShowcaseStory = () => { + + + Lines with different shapes + + + Area charts diff --git a/src/plugins/d3/examples/line/Shapes.tsx b/src/plugins/d3/examples/line/Shapes.tsx new file mode 100644 index 00000000..be877cfa --- /dev/null +++ b/src/plugins/d3/examples/line/Shapes.tsx @@ -0,0 +1,121 @@ +import React from 'react'; +import {ChartKitWidgetData, LineSeriesData, LineSeries, DashStyle} from '../../../../types'; +import {ChartKit} from '../../../../components/ChartKit'; +import nintendoGames from '../../examples/nintendoGames'; + +const SHAPES = { + [DashStyle.Solid]: 1, + [DashStyle.Dash]: 2, + [DashStyle.Dot]: 3, + [DashStyle.ShortDashDot]: 4, + [DashStyle.LongDash]: 5, + [DashStyle.LongDashDot]: 6, + [DashStyle.ShortDot]: 7, + [DashStyle.LongDashDotDot]: 8, + [DashStyle.ShortDash]: 9, + [DashStyle.DashDot]: 10, + [DashStyle.ShortDashDotDot]: 11, +}; + +const selectShapes = (): DashStyle[] => Object.values(DashStyle); +const getShapesOrder = () => selectShapes().sort((a, b) => SHAPES[a] - SHAPES[b]); + +const SHAPES_IN_ORDER = getShapesOrder(); + +function prepareData(): ChartKitWidgetData { + const games = nintendoGames.filter((d) => { + return d.date && d.user_score; + }); + + const byGenre = (genre: string) => { + return games + .filter((d) => d.genres.includes(genre)) + .map((d) => { + return { + x: d.date, + y: d.user_score, + label: d.title, + }; + }) as LineSeriesData[]; + }; + + return { + series: { + options: { + line: { + lineWidth: 2, + }, + }, + data: [ + { + name: '3D', + type: 'line', + data: byGenre('3D'), + dataLabels: { + enabled: true, + }, + }, + { + name: '2D', + type: 'line', + data: byGenre('2D'), + dataLabels: { + enabled: true, + }, + }, + { + name: 'Strategy', + type: 'line', + data: byGenre('Strategy'), + dataLabels: { + enabled: true, + }, + }, + { + name: 'Shooter', + type: 'line', + data: byGenre('Shooter'), + dataLabels: { + enabled: true, + }, + }, + ], + }, + xAxis: { + type: 'datetime', + title: { + text: 'Release date', + }, + }, + yAxis: [ + { + title: {text: 'User score'}, + labels: { + enabled: true, + }, + ticks: { + pixelInterval: 120, + }, + }, + ], + }; +} + +export const LinesWithShapes = () => { + const data = prepareData(); + + (data.series.data as LineSeries[]).forEach((graph, i) => { + graph.dashStyle = SHAPES_IN_ORDER[i % SHAPES_IN_ORDER.length]; + }); + + return ( +
+ +
+ ); +}; diff --git a/src/plugins/d3/renderer/hooks/useSeries/prepare-line-series.ts b/src/plugins/d3/renderer/hooks/useSeries/prepare-line-series.ts index f5fa1516..20747694 100644 --- a/src/plugins/d3/renderer/hooks/useSeries/prepare-line-series.ts +++ b/src/plugins/d3/renderer/hooks/useSeries/prepare-line-series.ts @@ -43,7 +43,7 @@ function prepareDashStyle(series: LineSeries) { } function prepareLinecap(series: LineSeries) { - return series.linecap || LineCap.None; + return series.linecap || (series.dashStyle === DashStyle.Solid ? LineCap.Round : LineCap.None); } function prepareLineLegendSymbol( diff --git a/src/types/widget-data/line.ts b/src/types/widget-data/line.ts index cd40a5ab..5ca951bd 100644 --- a/src/types/widget-data/line.ts +++ b/src/types/widget-data/line.ts @@ -46,6 +46,8 @@ export type LineSeries = BaseSeries & { }; /** Options for the point markers of line series */ marker?: LineMarkerOptions; + /** Option for line stroke style */ dashStyle?: DashStyle; + /** Option for line cap style */ linecap?: LineCap; }; diff --git a/src/types/widget-data/series.ts b/src/types/widget-data/series.ts index 99aa3bcf..8cfbe329 100644 --- a/src/types/widget-data/series.ts +++ b/src/types/widget-data/series.ts @@ -203,7 +203,8 @@ export type ChartKitWidgetSeriesOptions = { /** Options for line cap style * - * @default 'round' + * @default 'round' when dashStyle is 'solid' + * @default 'none' when dashStyle is not 'solid' * */ linecap?: LineCap; }; From ce895bfb27fe3360a396525978b64d336ad7e875 Mon Sep 17 00:00:00 2001 From: Artem Panchuk Date: Tue, 26 Dec 2023 17:21:13 +0300 Subject: [PATCH 07/10] Fix dashStyle passing from seriesOptions --- .../d3/renderer/hooks/useSeries/prepare-line-series.ts | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/plugins/d3/renderer/hooks/useSeries/prepare-line-series.ts b/src/plugins/d3/renderer/hooks/useSeries/prepare-line-series.ts index 20747694..3c5bbd3b 100644 --- a/src/plugins/d3/renderer/hooks/useSeries/prepare-line-series.ts +++ b/src/plugins/d3/renderer/hooks/useSeries/prepare-line-series.ts @@ -22,6 +22,7 @@ import {getRandomCKId} from '../../../../../utils'; export const DEFAULT_LEGEND_SYMBOL_SIZE = 16; export const DEFAULT_LINE_WIDTH = 1; +export const DEFAULT_DASH_STYLE = DashStyle.Solid; export const DEFAULT_MARKER = { enabled: false, @@ -38,10 +39,6 @@ type PrepareLineSeriesArgs = { legend: PreparedLegend; }; -function prepareDashStyle(series: LineSeries) { - return series.dashStyle || DashStyle.Solid; -} - function prepareLinecap(series: LineSeries) { return series.linecap || (series.dashStyle === DashStyle.Solid ? LineCap.Round : LineCap.None); } @@ -87,7 +84,9 @@ function prepareMarker(series: LineSeries, seriesOptions?: ChartKitWidgetSeriesO export function prepareLineSeries(args: PrepareLineSeriesArgs) { const {colorScale, series: seriesList, seriesOptions, legend} = args; + const defaultLineWidth = get(seriesOptions, 'line.lineWidth', DEFAULT_LINE_WIDTH); + const defaultDashStyle = get(seriesOptions, 'line.dashStyle', DEFAULT_DASH_STYLE); return seriesList.map((series) => { const id = getRandomCKId(); @@ -113,7 +112,7 @@ export function prepareLineSeries(args: PrepareLineSeriesArgs) { allowOverlap: get(series, 'dataLabels.allowOverlap', false), }, marker: prepareMarker(series, seriesOptions), - dashStyle: prepareDashStyle(series), + dashStyle: get(series, 'dashStyle', defaultDashStyle), linecap: prepareLinecap(series), }; From de6943818f3615529dca9dcf42ba838a931eaba7 Mon Sep 17 00:00:00 2001 From: Artem Panchuk Date: Tue, 26 Dec 2023 17:30:38 +0300 Subject: [PATCH 08/10] Fix linecap passing from seriesOptions --- .../hooks/useSeries/prepare-line-series.ts | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/src/plugins/d3/renderer/hooks/useSeries/prepare-line-series.ts b/src/plugins/d3/renderer/hooks/useSeries/prepare-line-series.ts index 3c5bbd3b..89983477 100644 --- a/src/plugins/d3/renderer/hooks/useSeries/prepare-line-series.ts +++ b/src/plugins/d3/renderer/hooks/useSeries/prepare-line-series.ts @@ -39,8 +39,15 @@ type PrepareLineSeriesArgs = { legend: PreparedLegend; }; -function prepareLinecap(series: LineSeries) { - return series.linecap || (series.dashStyle === DashStyle.Solid ? LineCap.Round : LineCap.None); +function prepareLinecap( + dashStyle: DashStyle, + series: LineSeries, + seriesOptions?: ChartKitWidgetSeriesOptions, +) { + const defaultLineCap = dashStyle === DashStyle.Solid ? LineCap.Round : LineCap.None; + const lineCapFromSeriesOptions = get(seriesOptions, 'line.linecap', defaultLineCap); + + return get(series, 'linecap', lineCapFromSeriesOptions); } function prepareLineLegendSymbol( @@ -92,6 +99,7 @@ export function prepareLineSeries(args: PrepareLineSeriesArgs) { const id = getRandomCKId(); const name = series.name || ''; const color = series.color || colorScale(name); + const dashStyle = get(series, 'dashStyle', defaultDashStyle); const prepared: PreparedLineSeries = { type: series.type, @@ -112,8 +120,8 @@ export function prepareLineSeries(args: PrepareLineSeriesArgs) { allowOverlap: get(series, 'dataLabels.allowOverlap', false), }, marker: prepareMarker(series, seriesOptions), - dashStyle: get(series, 'dashStyle', defaultDashStyle), - linecap: prepareLinecap(series), + dashStyle, + linecap: prepareLinecap(dashStyle, series, seriesOptions), }; return prepared; From 48f78274ad03d6d1a6743a93dab86acd8691adbb Mon Sep 17 00:00:00 2001 From: Artem Panchuk Date: Tue, 26 Dec 2023 17:31:05 +0300 Subject: [PATCH 09/10] Remove unneccessary story --- .../d3/__stories__/line/Shapes.stories.tsx | 173 ------------------ 1 file changed, 173 deletions(-) delete mode 100644 src/plugins/d3/__stories__/line/Shapes.stories.tsx diff --git a/src/plugins/d3/__stories__/line/Shapes.stories.tsx b/src/plugins/d3/__stories__/line/Shapes.stories.tsx deleted file mode 100644 index ae0f4055..00000000 --- a/src/plugins/d3/__stories__/line/Shapes.stories.tsx +++ /dev/null @@ -1,173 +0,0 @@ -import React from 'react'; -import {StoryObj} from '@storybook/react'; -import {Button} from '@gravity-ui/uikit'; -import {settings} from '../../../../libs'; -import {D3Plugin} from '../..'; -import {ChartKitWidgetData, LineSeriesData, LineSeries, DashStyle} from '../../../../types'; -import {ChartKit} from '../../../../components/ChartKit'; -import nintendoGames from '../../examples/nintendoGames'; -import {HighchartsPlugin} from '../../../highcharts'; - -const SHAPES = { - [DashStyle.Solid]: 1, - [DashStyle.Dash]: 2, - [DashStyle.Dot]: 3, - [DashStyle.ShortDashDot]: 4, - [DashStyle.LongDash]: 5, - [DashStyle.LongDashDot]: 6, - [DashStyle.ShortDot]: 7, - [DashStyle.LongDashDotDot]: 8, - [DashStyle.ShortDash]: 9, - [DashStyle.DashDot]: 10, - [DashStyle.ShortDashDotDot]: 11, -}; - -const selectShapes = (): DashStyle[] => Object.values(DashStyle); -const getShapesOrder = () => selectShapes().sort((a, b) => SHAPES[a] - SHAPES[b]); - -const SHAPES_IN_ORDER = getShapesOrder(); - -function prepareData(): ChartKitWidgetData { - const games = nintendoGames.filter((d) => { - return d.date && d.user_score; - }); - - const byGenre = (genre: string) => { - return games - .filter((d) => d.genres.includes(genre)) - .map((d) => { - return { - x: d.date, - y: d.user_score, - label: d.title, - }; - }) as LineSeriesData[]; - }; - - return { - series: { - options: { - line: { - lineWidth: 2, - }, - }, - data: [ - { - name: '3D', - type: 'line', - data: byGenre('3D'), - dataLabels: { - enabled: true, - }, - }, - { - name: '2D', - type: 'line', - data: byGenre('2D'), - dataLabels: { - enabled: true, - }, - }, - { - name: 'Strategy', - type: 'line', - data: byGenre('Strategy'), - dataLabels: { - enabled: true, - }, - }, - { - name: 'Shooter', - type: 'line', - data: byGenre('Shooter'), - dataLabels: { - enabled: true, - }, - }, - ], - }, - xAxis: { - type: 'datetime', - title: { - text: 'Release date', - }, - }, - yAxis: [ - { - title: {text: 'User score'}, - labels: { - enabled: true, - }, - ticks: { - pixelInterval: 120, - }, - }, - ], - }; -} - -const ChartStory = ({data}: {data: ChartKitWidgetData}) => { - const [shown, setShown] = React.useState(false); - - if (!shown) { - settings.set({plugins: [D3Plugin, HighchartsPlugin]}); - return ; - } - - (data.series.data as LineSeries[]).forEach((graph, i) => { - graph.dashStyle = SHAPES_IN_ORDER[i % SHAPES_IN_ORDER.length]; - }); - - return ( - <> -
- -
-
- -
- - ); -}; - -export const ShapesLineChartStory: StoryObj = { - name: 'Shapes', - args: { - data: prepareData(), - }, - argTypes: { - data: { - control: 'object', - }, - }, -}; - -export default { - title: 'Plugins/D3/Line', - component: ChartStory, -}; From 4774dd1e60d9417cc7a7cac8593ec3be41f7f138 Mon Sep 17 00:00:00 2001 From: Artem Panchuk Date: Wed, 27 Dec 2023 14:28:07 +0300 Subject: [PATCH 10/10] Minor pr fixes --- src/constants/index.ts | 2 ++ src/constants/widget-data.ts | 20 +++++++++++++ src/plugins/d3/examples/line/Shapes.tsx | 3 +- .../hooks/useSeries/prepare-line-series.ts | 8 ++--- .../d3/renderer/hooks/useSeries/types.ts | 3 +- .../d3/renderer/hooks/useShapes/line/types.ts | 3 +- .../hooks/useShapes/scatter/index.tsx | 12 ++++---- .../d3/renderer/hooks/useShapes/utils.ts | 4 ++- src/types/widget-data/line.ts | 6 ++-- src/types/widget-data/series.ts | 30 ++++--------------- 10 files changed, 49 insertions(+), 42 deletions(-) create mode 100644 src/constants/widget-data.ts diff --git a/src/constants/index.ts b/src/constants/index.ts index fe0e0e5e..36445340 100644 --- a/src/constants/index.ts +++ b/src/constants/index.ts @@ -1 +1,3 @@ export {CHARTKIT_SCROLLABLE_NODE_CLASSNAME} from './common'; + +export * from './widget-data'; diff --git a/src/constants/widget-data.ts b/src/constants/widget-data.ts new file mode 100644 index 00000000..b6f66e02 --- /dev/null +++ b/src/constants/widget-data.ts @@ -0,0 +1,20 @@ +export enum DashStyle { + Dash = 'Dash', + DashDot = 'DashDot', + Dot = 'Dot', + LongDash = 'LongDash', + LongDashDot = 'LongDashDot', + LongDashDotDot = 'LongDashDotDot', + ShortDash = 'ShortDash', + ShortDashDot = 'ShortDashDot', + ShortDashDotDot = 'ShortDashDotDot', + ShortDot = 'ShortDot', + Solid = 'Solid', +} + +export enum LineCap { + Butt = 'butt', + Round = 'round', + Square = 'square', + None = 'none', +} diff --git a/src/plugins/d3/examples/line/Shapes.tsx b/src/plugins/d3/examples/line/Shapes.tsx index be877cfa..bdf1a8e8 100644 --- a/src/plugins/d3/examples/line/Shapes.tsx +++ b/src/plugins/d3/examples/line/Shapes.tsx @@ -1,7 +1,8 @@ import React from 'react'; -import {ChartKitWidgetData, LineSeriesData, LineSeries, DashStyle} from '../../../../types'; +import {ChartKitWidgetData, LineSeriesData, LineSeries} from '../../../../types'; import {ChartKit} from '../../../../components/ChartKit'; import nintendoGames from '../../examples/nintendoGames'; +import {DashStyle} from '../../../../constants'; const SHAPES = { [DashStyle.Solid]: 1, diff --git a/src/plugins/d3/renderer/hooks/useSeries/prepare-line-series.ts b/src/plugins/d3/renderer/hooks/useSeries/prepare-line-series.ts index 89983477..6211cbf2 100644 --- a/src/plugins/d3/renderer/hooks/useSeries/prepare-line-series.ts +++ b/src/plugins/d3/renderer/hooks/useSeries/prepare-line-series.ts @@ -2,11 +2,11 @@ import {ScaleOrdinal} from 'd3'; import get from 'lodash/get'; import merge from 'lodash/merge'; +import {DashStyle, LineCap} from '../../../../../constants'; + import { ChartKitWidgetSeries, ChartKitWidgetSeriesOptions, - DashStyle, - LineCap, LineSeries, RectLegendSymbolOptions, } from '../../../../../types'; @@ -120,8 +120,8 @@ export function prepareLineSeries(args: PrepareLineSeriesArgs) { allowOverlap: get(series, 'dataLabels.allowOverlap', false), }, marker: prepareMarker(series, seriesOptions), - dashStyle, - linecap: prepareLinecap(dashStyle, series, seriesOptions), + dashStyle: dashStyle as DashStyle, + linecap: prepareLinecap(dashStyle as DashStyle, series, seriesOptions) as LineCap, }; return prepared; diff --git a/src/plugins/d3/renderer/hooks/useSeries/types.ts b/src/plugins/d3/renderer/hooks/useSeries/types.ts index 7aa3be00..b4249f51 100644 --- a/src/plugins/d3/renderer/hooks/useSeries/types.ts +++ b/src/plugins/d3/renderer/hooks/useSeries/types.ts @@ -15,12 +15,11 @@ import { ConnectorShape, ConnectorCurve, PathLegendSymbolOptions, - DashStyle, - LineCap, AreaSeries, AreaSeriesData, } from '../../../../../types'; import type {SeriesOptionsDefaults} from '../../constants'; +import {DashStyle, LineCap} from '../../../../../constants'; export type RectLegendSymbol = { shape: 'rect'; diff --git a/src/plugins/d3/renderer/hooks/useShapes/line/types.ts b/src/plugins/d3/renderer/hooks/useShapes/line/types.ts index dfc42c3d..9c1ed1e6 100644 --- a/src/plugins/d3/renderer/hooks/useShapes/line/types.ts +++ b/src/plugins/d3/renderer/hooks/useShapes/line/types.ts @@ -1,6 +1,7 @@ import {PreparedLineSeries} from '../../useSeries/types'; -import {DashStyle, LineCap, LineSeriesData} from '../../../../../../types'; +import {LineSeriesData} from '../../../../../../types'; import {LabelData} from '../../../types'; +import {DashStyle, LineCap} from '../../../../../../constants'; export type PointData = { x: number; diff --git a/src/plugins/d3/renderer/hooks/useShapes/scatter/index.tsx b/src/plugins/d3/renderer/hooks/useShapes/scatter/index.tsx index 8dd669f9..4073aa07 100644 --- a/src/plugins/d3/renderer/hooks/useShapes/scatter/index.tsx +++ b/src/plugins/d3/renderer/hooks/useShapes/scatter/index.tsx @@ -48,17 +48,19 @@ export function ScatterSeriesShape(props: ScatterSeriesShapeProps) { const inactiveOptions = get(seriesOptions, 'scatter.states.inactive'); const selection = svgElement - .selectAll(`circle`) + .selectAll('point') .data(preparedData, shapeKey) .join( - (enter) => enter.append('circle').attr('class', b('point')), + (enter) => enter.append('rect').attr('class', b('point')), (update) => update, (exit) => exit.remove(), ) .attr('fill', (d) => d.data.color || d.series.color || '') - .attr('r', (d) => d.data.radius || DEFAULT_SCATTER_POINT_RADIUS) - .attr('cx', (d) => d.cx) - .attr('cy', (d) => d.cy); + // .attr('r', (d) => d.data.radius || DEFAULT_SCATTER_POINT_RADIUS) + .attr('x', (d) => d.cx) + .attr('y', (d) => d.cy) + .attr('width', () => DEFAULT_SCATTER_POINT_RADIUS) + .attr('height', () => DEFAULT_SCATTER_POINT_RADIUS); svgElement .on('mousemove', (e) => { diff --git a/src/plugins/d3/renderer/hooks/useShapes/utils.ts b/src/plugins/d3/renderer/hooks/useShapes/utils.ts index 25f25985..9bdb4158 100644 --- a/src/plugins/d3/renderer/hooks/useShapes/utils.ts +++ b/src/plugins/d3/renderer/hooks/useShapes/utils.ts @@ -2,13 +2,15 @@ import type {BaseType, ScaleBand, ScaleLinear, ScaleTime} from 'd3'; import {select} from 'd3'; import get from 'lodash/get'; -import type {BasicInactiveState, DashStyle} from '../../../../../types'; +import type {BasicInactiveState} from '../../../../../types'; import {getDataCategoryValue} from '../../utils'; import type {ChartScale} from '../useAxisScales'; import type {PreparedAxis} from '../useChartOptions/types'; import type {PreparedLineData} from './line/types'; import type {PreparedScatterData} from './scatter'; +import {DashStyle} from '../../../../../constants'; + export function getXValue(args: { point: {x?: number | string}; xAxis: PreparedAxis; diff --git a/src/types/widget-data/line.ts b/src/types/widget-data/line.ts index 5ca951bd..f3cf85f7 100644 --- a/src/types/widget-data/line.ts +++ b/src/types/widget-data/line.ts @@ -1,7 +1,7 @@ import type {BaseSeries, BaseSeriesData} from './base'; import type {ChartKitWidgetLegend, RectLegendSymbolOptions} from './legend'; import type {PointMarkerOptions} from './marker'; -import type {DashStyle, LineCap} from './series'; +import {DashStyle, LineCap} from '../../constants'; export type LineSeriesData = BaseSeriesData & { /** @@ -47,7 +47,7 @@ export type LineSeries = BaseSeries & { /** Options for the point markers of line series */ marker?: LineMarkerOptions; /** Option for line stroke style */ - dashStyle?: DashStyle; + dashStyle?: `${DashStyle}`; /** Option for line cap style */ - linecap?: LineCap; + linecap?: `${LineCap}`; }; diff --git a/src/types/widget-data/series.ts b/src/types/widget-data/series.ts index 8cfbe329..7fddefd0 100644 --- a/src/types/widget-data/series.ts +++ b/src/types/widget-data/series.ts @@ -7,6 +7,8 @@ import type {BarYSeries, BarYSeriesData} from './bar-y'; import type {PointMarkerOptions, PointMarkerHalo} from './marker'; import type {AreaSeries, AreaSeriesData} from './area'; +import {DashStyle, LineCap} from '../../constants'; + export type ChartKitWidgetSeries = | ScatterSeries | PieSeries @@ -27,27 +29,6 @@ export type DataLabelRendererData = { data: ChartKitWidgetSeriesData; }; -export enum DashStyle { - Dash = 'Dash', - DashDot = 'DashDot', - Dot = 'Dot', - LongDash = 'LongDash', - LongDashDot = 'LongDashDot', - LongDashDotDot = 'LongDashDotDot', - ShortDash = 'ShortDash', - ShortDashDot = 'ShortDashDot', - ShortDashDotDot = 'ShortDashDotDot', - ShortDot = 'ShortDot', - Solid = 'Solid', -} - -export enum LineCap { - Butt = 'butt', - Round = 'round', - Square = 'square', - None = 'none', -} - type BasicHoverState = { /** * Enable separate styles for the hovered series. @@ -199,14 +180,13 @@ export type ChartKitWidgetSeriesOptions = { * * @default 'Solid' * */ - dashStyle?: DashStyle; + dashStyle?: `${DashStyle}`; /** Options for line cap style * - * @default 'round' when dashStyle is 'solid' - * @default 'none' when dashStyle is not 'solid' + * @default 'round' when dashStyle is not 'solid', 'none' when dashStyle is not 'solid' * */ - linecap?: LineCap; + linecap?: `${LineCap}`; }; area?: { /** Pixel width of the graph line.