diff --git a/.playground/playgroud.tsx b/.playground/playgroud.tsx index a5ec36d981..4a9f51fa9f 100644 --- a/.playground/playgroud.tsx +++ b/.playground/playgroud.tsx @@ -5,12 +5,12 @@ import { Chart, getAxisId, getSpecId, - LineSeries, niceTimeFormatter, Position, ScaleType, Settings, mergeWithDefaultTheme, + AreaSeries, } from '../src'; import { KIBANA_METRICS } from '../src/utils/data_samples/test_dataset_kibana'; @@ -21,10 +21,6 @@ export class Playground extends React.Component { renderChart(legendPosition: Position) { const theme = mergeWithDefaultTheme({ lineSeriesStyle: { - // area: { - // fill: 'green', - // opacity:0.2 - // }, line: { stroke: 'violet', strokeWidth: 4, @@ -49,28 +45,64 @@ export class Playground extends React.Component { tickFormat={niceTimeFormatter([1555819200000, 1555905600000])} /> d.toFixed(2)} /> - - + diff --git a/src/chart_types/xy_chart/rendering/rendering.ts b/src/chart_types/xy_chart/rendering/rendering.ts index 8c1282f025..9f172cb13f 100644 --- a/src/chart_types/xy_chart/rendering/rendering.ts +++ b/src/chart_types/xy_chart/rendering/rendering.ts @@ -92,6 +92,7 @@ export interface AreaGeometry { seriesAreaStyle: AreaStyle; seriesAreaLineStyle: LineStyle; seriesPointStyle: PointStyle; + isStacked: boolean; } export function isPointGeometry(ig: IndexedGeometry): ig is PointGeometry { @@ -411,6 +412,7 @@ export function renderArea( seriesKey: any[], xScaleOffset: number, seriesStyle: AreaSeriesStyle, + isStacked: boolean = false, ): { areaGeometry: AreaGeometry; indexedGeometries: Map; @@ -469,6 +471,7 @@ export function renderArea( seriesAreaStyle: seriesStyle.area, seriesAreaLineStyle: seriesStyle.line, seriesPointStyle: seriesStyle.point, + isStacked, }; return { areaGeometry, @@ -527,3 +530,7 @@ export function isPointOnGeometry( const { width, height } = indexedGeometry; return yCoordinate >= y && yCoordinate <= y + height && xCoordinate >= x && xCoordinate <= x + width; } + +export function getGeometryIdKey(geometryId: GeometryId, prefix?: string, postfix?: string) { + return `${prefix || ''}spec:${geometryId.specId}_${geometryId.seriesKey.join('::-::')}${postfix || ''}`; +} diff --git a/src/chart_types/xy_chart/store/utils.ts b/src/chart_types/xy_chart/store/utils.ts index 4d82e31b2a..f3a45bb76a 100644 --- a/src/chart_types/xy_chart/store/utils.ts +++ b/src/chart_types/xy_chart/store/utils.ts @@ -503,6 +503,7 @@ export function renderGeometries( ds.key, xScaleOffset, areaSeriesStyle, + isStacked, ); areaGeometriesIndex = mergeGeometriesIndexes(areaGeometriesIndex, renderedAreas.indexedGeometries); areas.push(renderedAreas.areaGeometry); diff --git a/src/components/react_canvas/area_geometries.tsx b/src/components/react_canvas/area_geometries.tsx index a316ade587..5bd600f1f8 100644 --- a/src/components/react_canvas/area_geometries.tsx +++ b/src/components/react_canvas/area_geometries.tsx @@ -1,9 +1,14 @@ import { Group as KonvaGroup } from 'konva'; import React from 'react'; import { Circle, Group, Path } from 'react-konva'; -import { animated, Spring } from 'react-spring/renderprops-konva.cjs'; import { LegendItem } from '../../chart_types/xy_chart/legend/legend'; -import { AreaGeometry, getGeometryStyle, PointGeometry } from '../../chart_types/xy_chart/rendering/rendering'; +import { + AreaGeometry, + getGeometryStyle, + PointGeometry, + getGeometryIdKey, + GeometryId, +} from '../../chart_types/xy_chart/rendering/rendering'; import { SharedGeometryStyle } from '../../utils/themes/theme'; import { buildAreaRenderProps, @@ -38,110 +43,118 @@ export class AreaGeometries extends React.PureComponent {this.renderAreaGeoms()} - {this.renderAreaLines()} - {this.renderAreaPoints()} ); } - private renderAreaPoints = (): JSX.Element[] => { - const { areas } = this.props; - return areas.reduce( - (acc, glyph, i) => { - const { points, seriesPointStyle, color } = glyph; - - if (!seriesPointStyle.visible) { - return acc; + private renderAreaGeoms = (): JSX.Element[] => { + const { sharedStyle, highlightedLegendItem } = this.props; + const areas = this.props.areas.reduce<{ + stacked: AreaGeometry[]; + nonStacked: AreaGeometry[]; + }>( + (acc, area) => { + if (area.isStacked) { + acc.stacked.push(area); + } else { + acc.nonStacked.push(area); } - - const pointStyleProps = buildPointStyleProps(color, seriesPointStyle); - - return [...acc, ...this.renderPoints(points, i, pointStyleProps)]; + return acc; }, - [] as JSX.Element[], + + { stacked: [], nonStacked: [] }, ); + + return [ + ...this.renderStackedAreas(areas.stacked, sharedStyle, highlightedLegendItem), + ...this.renderNonStackedAreas(areas.nonStacked, sharedStyle, highlightedLegendItem), + ]; }; - private renderPoints = ( - areaPoints: PointGeometry[], - areaIndex: number, - pointStyleProps: PointStyleProps, + renderStackedAreas = ( + areas: AreaGeometry[], + sharedStyle: SharedGeometryStyle, + highlightedLegendItem: LegendItem | null, ): JSX.Element[] => { - const areaPointElements: JSX.Element[] = []; - - areaPoints.forEach((areaPoint, pointIndex) => { - const { x, y, transform } = areaPoint; - const key = `area-point-${areaIndex}-${pointIndex}`; - - if (this.props.animated) { - areaPointElements.push( - - - {() => { - const pointProps = buildPointRenderProps(x, y, pointStyleProps); - return ; - }} - - , - ); - } else { - const pointProps = buildPointRenderProps(transform.x + x, y, pointStyleProps); - areaPointElements.push(); + const elements: JSX.Element[] = []; + areas.forEach((glyph) => { + const { seriesAreaStyle } = glyph; + if (seriesAreaStyle.visible) { + elements.push(this.renderArea(glyph, sharedStyle, highlightedLegendItem)); } }); - return areaPointElements; - }; - - private renderAreaGeoms = (): JSX.Element[] => { - const { areas, sharedStyle } = this.props; - const areasToRender: JSX.Element[] = []; - areas.forEach((glyph, i) => { - const { area, color, transform, geometryId, seriesAreaStyle } = glyph; - if (!seriesAreaStyle.visible) { - return; + const { seriesAreaLineStyle } = glyph; + if (seriesAreaLineStyle.visible) { + elements.push(...this.renderAreaLines(glyph, i, sharedStyle, highlightedLegendItem)); } - const customOpacity = seriesAreaStyle ? seriesAreaStyle.opacity : undefined; - const geometryStyle = getGeometryStyle(geometryId, this.props.highlightedLegendItem, sharedStyle, customOpacity); - const key = `area-${i}`; - if (this.props.animated) { - areasToRender.push( - - - {(props: { area: string }) => { - const areaProps = buildAreaRenderProps(0, props.area, color, seriesAreaStyle, geometryStyle); - return ; - }} - - , - ); - } else { - const areaProps = buildAreaRenderProps(transform.x, area, color, seriesAreaStyle, geometryStyle); - areasToRender.push(); + }); + areas.forEach((glyph, i) => { + const { seriesPointStyle } = glyph; + if (seriesPointStyle.visible) { + const pointStyleProps = buildPointStyleProps(glyph.color, seriesPointStyle); + elements.push(...this.renderPoints(glyph.points, i, pointStyleProps, glyph.geometryId)); } }); - return areasToRender; + return elements; }; - private renderAreaLines = (): JSX.Element[] => { - const { areas, sharedStyle } = this.props; - const linesToRender: JSX.Element[] = []; - areas.forEach((glyph, areaIndex) => { - const { lines, color, geometryId, transform, seriesAreaLineStyle } = glyph; - if (!seriesAreaLineStyle.visible) { - return; + renderNonStackedAreas = ( + areas: AreaGeometry[], + sharedStyle: SharedGeometryStyle, + highlightedLegendItem: LegendItem | null, + ): JSX.Element[] => { + return areas.reduce((acc, glyph, i) => { + const { seriesAreaLineStyle, seriesAreaStyle, seriesPointStyle } = glyph; + if (seriesAreaStyle.visible) { + acc.push(this.renderArea(glyph, sharedStyle, highlightedLegendItem)); + } + if (seriesAreaLineStyle.visible) { + acc.push(...this.renderAreaLines(glyph, i, sharedStyle, highlightedLegendItem)); + } + if (seriesPointStyle.visible) { + const pointStyleProps = buildPointStyleProps(glyph.color, seriesPointStyle); + acc.push(...this.renderPoints(glyph.points, i, pointStyleProps, glyph.geometryId)); } + return acc; + }, []); + }; + private renderArea = ( + glyph: AreaGeometry, + sharedStyle: SharedGeometryStyle, + highlightedLegendItem: LegendItem | null, + ): JSX.Element => { + const { area, color, transform, geometryId, seriesAreaStyle } = glyph; + const customOpacity = seriesAreaStyle ? seriesAreaStyle.opacity : undefined; + const geometryStyle = getGeometryStyle(geometryId, highlightedLegendItem, sharedStyle, customOpacity); + const key = getGeometryIdKey(geometryId, 'area-'); + const areaProps = buildAreaRenderProps(transform.x, area, color, seriesAreaStyle, geometryStyle); + return ; + }; + private renderAreaLines = ( + glyph: AreaGeometry, + areaIndex: number, + sharedStyle: SharedGeometryStyle, + highlightedLegendItem: LegendItem | null, + ): JSX.Element[] => { + const { lines, color, geometryId, transform, seriesAreaLineStyle } = glyph; + const geometryStyle = getGeometryStyle(geometryId, highlightedLegendItem, sharedStyle, seriesAreaLineStyle.opacity); - const geometryStyle = getGeometryStyle( - geometryId, - this.props.highlightedLegendItem, - sharedStyle, - seriesAreaLineStyle.opacity, - ); + return lines.map((linePath, lineIndex) => { + const key = getGeometryIdKey(geometryId, `area-line-${areaIndex}-${lineIndex}`); + const lineProps = buildLineRenderProps(transform.x, linePath, color, seriesAreaLineStyle, geometryStyle); + return ; + }); + }; - lines.forEach((linePath, lineIndex) => { - const key = `area-${areaIndex}-line-${lineIndex}`; - const lineProps = buildLineRenderProps(transform.x, linePath, color, seriesAreaLineStyle, geometryStyle); - linesToRender.push(); - }); + private renderPoints = ( + areaPoints: PointGeometry[], + areaIndex: number, + pointStyleProps: PointStyleProps, + geometryId: GeometryId, + ): JSX.Element[] => { + return areaPoints.map((areaPoint, pointIndex) => { + const { x, y, transform } = areaPoint; + const key = getGeometryIdKey(geometryId, `area-point-${areaIndex}-${pointIndex}-`); + const pointProps = buildPointRenderProps(transform.x + x, y, pointStyleProps); + return ; }); - return linesToRender; }; } diff --git a/src/components/react_canvas/line_geometries.tsx b/src/components/react_canvas/line_geometries.tsx index b9fa6e8152..f49f1afe1f 100644 --- a/src/components/react_canvas/line_geometries.tsx +++ b/src/components/react_canvas/line_geometries.tsx @@ -1,9 +1,13 @@ import { Group as KonvaGroup } from 'konva'; import React from 'react'; import { Circle, Group, Path } from 'react-konva'; -import { animated, Spring } from 'react-spring/renderprops-konva.cjs'; import { LegendItem } from '../../chart_types/xy_chart/legend/legend'; -import { getGeometryStyle, LineGeometry, PointGeometry } from '../../chart_types/xy_chart/rendering/rendering'; +import { + getGeometryStyle, + LineGeometry, + PointGeometry, + getGeometryIdKey, +} from '../../chart_types/xy_chart/rendering/rendering'; import { SharedGeometryStyle } from '../../utils/themes/theme'; import { buildLineRenderProps, @@ -38,51 +42,21 @@ export class LineGeometries extends React.PureComponent {this.renderLineGeoms()} - {this.renderLinePoints()} ); } - private renderLinePoints = (): JSX.Element[] => { - const { lines } = this.props; - return lines.reduce( - (acc, glyph, i) => { - const { points, seriesPointStyle, color } = glyph; - - if (!seriesPointStyle.visible) { - return acc; - } - const pointStyleProps = buildPointStyleProps(color, seriesPointStyle); - return [...acc, ...this.renderPoints(points, i, pointStyleProps)]; - }, - [] as JSX.Element[], - ); - }; - private renderPoints = ( linePoints: PointGeometry[], - lineIndex: number, + lineKey: string, pointStyleProps: PointStyleProps, ): JSX.Element[] => { const linePointsElements: JSX.Element[] = []; linePoints.forEach((linePoint, pointIndex) => { const { x, y, transform } = linePoint; - const key = `line-point-${lineIndex}-${pointIndex}`; - if (this.props.animated) { - linePointsElements.push( - - - {() => { - const pointProps = buildPointRenderProps(x, y, pointStyleProps); - return ; - }} - - , - ); - } else { - const pointProps = buildPointRenderProps(transform.x + x, y, pointStyleProps); - linePointsElements.push(); - } + const key = `line-point-${lineKey}-${pointIndex}`; + const pointProps = buildPointRenderProps(transform.x + x, y, pointStyleProps); + linePointsElements.push(); }); return linePointsElements; }; @@ -90,35 +64,32 @@ export class LineGeometries extends React.PureComponent { const { lines, sharedStyle } = this.props; - const lineElements: JSX.Element[] = []; - - lines.forEach((glyph, index) => { - const { line, color, transform, geometryId, seriesLineStyle } = glyph; - - if (!seriesLineStyle.visible) { - return; + return lines.reduce((acc, glyph) => { + const { seriesLineStyle, seriesPointStyle, geometryId } = glyph; + const key = getGeometryIdKey(geometryId, 'line-'); + if (seriesLineStyle.visible) { + acc.push(this.getLineToRender(glyph, sharedStyle, key)); } - const key = `line-${index}`; - const customOpacity = seriesLineStyle ? seriesLineStyle.opacity : undefined; - const geometryStyle = getGeometryStyle(geometryId, this.props.highlightedLegendItem, sharedStyle, customOpacity); - if (this.props.animated) { - lineElements.push( - - - {() => { - const lineProps = buildLineRenderProps(0, line, color, seriesLineStyle, geometryStyle); - return ; - }} - - , - ); - } else { - const lineProps = buildLineRenderProps(transform.x, line, color, seriesLineStyle, geometryStyle); - lineElements.push(); + if (seriesPointStyle.visible) { + acc.push(...this.getPointToRender(glyph, key)); } - }); - - return lineElements; + return acc; + }, []); }; + + getLineToRender(glyph: LineGeometry, sharedStyle: SharedGeometryStyle, key: string) { + const { line, color, transform, geometryId, seriesLineStyle } = glyph; + const customOpacity = seriesLineStyle ? seriesLineStyle.opacity : undefined; + const geometryStyle = getGeometryStyle(geometryId, this.props.highlightedLegendItem, sharedStyle, customOpacity); + const lineProps = buildLineRenderProps(transform.x, line, color, seriesLineStyle, geometryStyle); + return ; + } + + getPointToRender(glyph: LineGeometry, key: string) { + const { points, color, seriesPointStyle } = glyph; + + const pointStyleProps = buildPointStyleProps(color, seriesPointStyle); + return this.renderPoints(points, key, pointStyleProps); + } }