diff --git a/src/plugins/d3/__stories__/Showcase.stories.tsx b/src/plugins/d3/__stories__/Showcase.stories.tsx
index 535725e3..658e4bcd 100644
--- a/src/plugins/d3/__stories__/Showcase.stories.tsx
+++ b/src/plugins/d3/__stories__/Showcase.stories.tsx
@@ -41,9 +41,10 @@ const ShowcaseStory = () => {
- Line chart with data labels
+ With data labels
+
Bar-x charts
@@ -99,6 +100,7 @@ const ShowcaseStory = () => {
Donut chart
+
Scatter charts
@@ -108,6 +110,8 @@ const ShowcaseStory = () => {
Basic scatter
+
+
Combined chart
diff --git a/src/plugins/d3/renderer/components/Legend.tsx b/src/plugins/d3/renderer/components/Legend.tsx
index 60274ae5..2a7c6d0f 100644
--- a/src/plugins/d3/renderer/components/Legend.tsx
+++ b/src/plugins/d3/renderer/components/Legend.tsx
@@ -1,5 +1,5 @@
import React from 'react';
-import {select} from 'd3';
+import {BaseType, select, line as lineGenerator} from 'd3';
import type {Selection} from 'd3';
import {block} from '../../../../utils/cn';
@@ -94,6 +94,71 @@ const appendPaginator = (args: {
paginationLine.attr('transform', transform);
};
+const legendSymbolGenerator = lineGenerator<{x: number; y: number}>()
+ .x((d) => d.x)
+ .y((d) => d.y);
+
+function renderLegendSymbol(args: {
+ selection: Selection;
+ legend: PreparedLegend;
+}) {
+ const {selection, legend} = args;
+ const line = selection.data();
+
+ const getXPosition = (i: number) => {
+ return line.slice(0, i).reduce((acc, legendItem) => {
+ return (
+ acc +
+ legendItem.symbol.width +
+ legendItem.symbol.padding +
+ legendItem.textWidth +
+ legend.itemDistance
+ );
+ }, 0);
+ };
+
+ selection.each(function (d, i) {
+ const element = select(this);
+ const x = getXPosition(i);
+ const className = b('item-symbol', {shape: d.symbol.shape, unselected: !d.visible});
+ const color = d.visible ? d.color : '';
+
+ switch (d.symbol.shape) {
+ case 'path': {
+ const y = legend.lineHeight / 2;
+ const points = [
+ {x: x, y},
+ {x: x + d.symbol.width, y},
+ ];
+
+ element
+ .append('path')
+ .attr('d', legendSymbolGenerator(points))
+ .attr('fill', 'none')
+ .attr('stroke-width', d.symbol.strokeWidth)
+ .attr('class', className)
+ .style('stroke', color);
+
+ break;
+ }
+ case 'rect': {
+ const y = (legend.lineHeight - d.symbol.height) / 2;
+ element
+ .append('rect')
+ .attr('x', x)
+ .attr('y', y)
+ .attr('width', d.symbol.width)
+ .attr('height', d.symbol.height)
+ .attr('rx', d.symbol.radius)
+ .attr('class', className)
+ .style('fill', color);
+
+ break;
+ }
+ }
+ });
+}
+
export const Legend = (props: Props) => {
const {boundsWidth, chartSeries, legend, items, config, onItemClick} = props;
const ref = React.useRef(null);
@@ -139,25 +204,7 @@ export const Legend = (props: Props) => {
}, 0);
};
- legendItemTemplate
- .append('rect')
- .attr('x', function (_d, i) {
- return getXPosition(i);
- })
- .attr('y', (legendItem) => {
- return (legend.lineHeight - legendItem.symbol.height) / 2;
- })
- .attr('width', (legendItem) => {
- return legendItem.symbol.width;
- })
- .attr('height', (legendItem) => legendItem.symbol.height)
- .attr('rx', (legendItem) => legendItem.symbol.radius)
- .attr('class', function (d) {
- return b('item-shape', {unselected: !d.visible});
- })
- .style('fill', function (d) {
- return d.visible ? d.color : '';
- });
+ renderLegendSymbol({selection: legendItemTemplate, legend});
legendItemTemplate
.append('text')
diff --git a/src/plugins/d3/renderer/components/styles.scss b/src/plugins/d3/renderer/components/styles.scss
index 599156a9..9ef8b469 100644
--- a/src/plugins/d3/renderer/components/styles.scss
+++ b/src/plugins/d3/renderer/components/styles.scss
@@ -24,12 +24,14 @@
user-select: none;
}
- &__item-shape {
- fill: var(--g-color-base-misc-medium);
-
- &_unselected {
+ &__item-symbol {
+ &_shape_rect#{&}_unselected {
fill: var(--g-color-text-hint);
}
+
+ &_shape_path#{&}_unselected {
+ stroke: var(--g-color-text-hint);
+ }
}
&__item-text {
diff --git a/src/plugins/d3/renderer/hooks/useSeries/constants.ts b/src/plugins/d3/renderer/hooks/useSeries/constants.ts
index 9a2e271e..ec15021e 100644
--- a/src/plugins/d3/renderer/hooks/useSeries/constants.ts
+++ b/src/plugins/d3/renderer/hooks/useSeries/constants.ts
@@ -2,6 +2,8 @@ import {BaseTextStyle} from '../../../../../types';
export const DEFAULT_LEGEND_SYMBOL_SIZE = 8;
+export const DEFAULT_LEGEND_SYMBOL_PADDING = 5;
+
export const DEFAULT_DATALABELS_PADDING = 5;
export const DEFAULT_DATALABELS_STYLE: BaseTextStyle = {
diff --git a/src/plugins/d3/renderer/hooks/useSeries/prepare-legend.ts b/src/plugins/d3/renderer/hooks/useSeries/prepare-legend.ts
index 1776e436..a88a237c 100644
--- a/src/plugins/d3/renderer/hooks/useSeries/prepare-legend.ts
+++ b/src/plugins/d3/renderer/hooks/useSeries/prepare-legend.ts
@@ -3,7 +3,7 @@ import get from 'lodash/get';
import merge from 'lodash/merge';
import {select} from 'd3';
-import type {ChartKitWidgetData} from '../../../../../types/widget-data';
+import type {ChartKitWidgetData} from '../../../../../types';
import {legendDefaults} from '../../constants';
import {getHorisontalSvgTextHeight} from '../../utils';
@@ -24,6 +24,7 @@ export const getPreparedLegend = (args: {
const itemStyle = get(legend, 'itemStyle');
const computedItemStyle = merge(defaultItemStyle, itemStyle);
const lineHeight = getHorisontalSvgTextHeight({text: 'Tmp', style: computedItemStyle});
+
const height = enabled ? lineHeight : 0;
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 86d8db24..37ab442b 100644
--- a/src/plugins/d3/renderer/hooks/useSeries/prepare-line-series.ts
+++ b/src/plugins/d3/renderer/hooks/useSeries/prepare-line-series.ts
@@ -1,13 +1,24 @@
import {ScaleOrdinal} from 'd3';
import get from 'lodash/get';
-import {ChartKitWidgetSeriesOptions, LineSeries} from '../../../../../types';
-import {PreparedLineSeries, PreparedLegend, PreparedSeries} from './types';
+import {
+ ChartKitWidgetSeries,
+ ChartKitWidgetSeriesOptions,
+ LineSeries,
+ RectLegendSymbolOptions,
+} from '../../../../../types';
+import {PreparedLineSeries, PreparedLegend, PreparedSeries, PreparedLegendSymbol} from './types';
-import {DEFAULT_DATALABELS_PADDING, DEFAULT_DATALABELS_STYLE} from './constants';
-import {prepareLegendSymbol} from './utils';
+import {
+ DEFAULT_DATALABELS_PADDING,
+ DEFAULT_DATALABELS_STYLE,
+ DEFAULT_LEGEND_SYMBOL_PADDING,
+} from './constants';
import {getRandomCKId} from '../../../../../utils';
+export const DEFAULT_LEGEND_SYMBOL_SIZE = 16;
+export const DEFAULT_LINE_WIDTH = 1;
+
type PrepareLineSeriesArgs = {
colorScale: ScaleOrdinal;
series: LineSeries[];
@@ -15,9 +26,24 @@ type PrepareLineSeriesArgs = {
legend: PreparedLegend;
};
+function prepareLineLegendSymbol(
+ series: ChartKitWidgetSeries,
+ seriesOptions?: ChartKitWidgetSeriesOptions,
+): PreparedLegendSymbol {
+ const symbolOptions: RectLegendSymbolOptions = series.legend?.symbol || {};
+ const defaultLineWidth = get(seriesOptions, 'line.lineWidth', DEFAULT_LINE_WIDTH);
+
+ return {
+ shape: 'path',
+ width: symbolOptions?.width || DEFAULT_LEGEND_SYMBOL_SIZE,
+ padding: symbolOptions?.padding || DEFAULT_LEGEND_SYMBOL_PADDING,
+ strokeWidth: get(series, 'lineWidth', defaultLineWidth),
+ };
+}
+
export function prepareLineSeries(args: PrepareLineSeriesArgs): PreparedSeries[] {
const {colorScale, series: seriesList, seriesOptions, legend} = args;
- const defaultLineWidth = get(seriesOptions, 'line.lineWidth', 1);
+ const defaultLineWidth = get(seriesOptions, 'line.lineWidth', DEFAULT_LINE_WIDTH);
return seriesList.map((series) => {
const id = getRandomCKId();
@@ -33,7 +59,7 @@ export function prepareLineSeries(args: PrepareLineSeriesArgs): PreparedSeries[]
visible: get(series, 'visible', true),
legend: {
enabled: get(series, 'legend.enabled', legend.enabled),
- symbol: prepareLegendSymbol(series),
+ symbol: prepareLineLegendSymbol(series, seriesOptions),
},
data: series.data,
dataLabels: {
diff --git a/src/plugins/d3/renderer/hooks/useSeries/types.ts b/src/plugins/d3/renderer/hooks/useSeries/types.ts
index 9540d9ff..0548778f 100644
--- a/src/plugins/d3/renderer/hooks/useSeries/types.ts
+++ b/src/plugins/d3/renderer/hooks/useSeries/types.ts
@@ -14,6 +14,7 @@ import {
LineSeriesData,
ConnectorShape,
ConnectorCurve,
+ PathLegendSymbolOptions,
} from '../../../../../types';
import type {SeriesOptionsDefaults} from '../../constants';
@@ -21,7 +22,12 @@ export type RectLegendSymbol = {
shape: 'rect';
} & Required;
-export type PreparedLegendSymbol = RectLegendSymbol;
+export type PathLegendSymbol = {
+ shape: 'path';
+ strokeWidth: number;
+} & Required;
+
+export type PreparedLegendSymbol = RectLegendSymbol | PathLegendSymbol;
export type PreparedLegend = Required & {
height: number;
diff --git a/src/plugins/d3/renderer/hooks/useSeries/utils.ts b/src/plugins/d3/renderer/hooks/useSeries/utils.ts
index d525c338..50e4eda3 100644
--- a/src/plugins/d3/renderer/hooks/useSeries/utils.ts
+++ b/src/plugins/d3/renderer/hooks/useSeries/utils.ts
@@ -1,6 +1,6 @@
import {PreparedLegendSymbol, PreparedSeries} from './types';
import {ChartKitWidgetSeries, RectLegendSymbolOptions} from '../../../../../types';
-import {DEFAULT_LEGEND_SYMBOL_SIZE} from './constants';
+import {DEFAULT_LEGEND_SYMBOL_PADDING, DEFAULT_LEGEND_SYMBOL_SIZE} from './constants';
export const getActiveLegendItems = (series: PreparedSeries[]) => {
return series.reduce((acc, s) => {
@@ -25,6 +25,6 @@ export function prepareLegendSymbol(series: ChartKitWidgetSeries): PreparedLegen
width: symbolOptions?.width || DEFAULT_LEGEND_SYMBOL_SIZE,
height: symbolHeight,
radius: symbolOptions?.radius || symbolHeight / 2,
- padding: symbolOptions?.padding || 5,
+ padding: symbolOptions?.padding || DEFAULT_LEGEND_SYMBOL_PADDING,
};
}
diff --git a/src/types/widget-data/legend.ts b/src/types/widget-data/legend.ts
index c1c3f68a..528f9a2c 100644
--- a/src/types/widget-data/legend.ts
+++ b/src/types/widget-data/legend.ts
@@ -57,3 +57,12 @@ export type RectLegendSymbolOptions = BaseLegendSymbol & {
*/
radius?: number;
};
+
+export type PathLegendSymbolOptions = BaseLegendSymbol & {
+ /**
+ * The pixel width of the symbol for series types that use a path in the legend
+ *
+ * @default 16
+ * */
+ width?: number;
+};