*/
import classNames from 'classnames';
import { bisector, extent, max } from 'd3-array';
-import { scaleLinear, scalePoint, scaleTime, ScaleTime } from 'd3-scale';
+import {
+ NumberValue,
+ ScaleLinear,
+ scaleLinear,
+ ScalePoint,
+ scalePoint,
+ scaleTime,
+ ScaleTime,
+} from 'd3-scale';
import { area, curveBasis, line as d3Line } from 'd3-shape';
import { flatten, isEqual, sortBy, throttle, uniq } from 'lodash';
import * as React from 'react';
}
type XScale = ScaleTime<number, number>;
-// TODO it should be `ScaleLinear<number, number> | ScalePoint<number> | ScalePoint<string>`, but it's super hard to make it work :'(
-type YScale = any;
+type YScale = ScaleLinear<number, number> | ScalePoint<number | string>;
+type YPoint = (number | string) & NumberValue;
const LEGEND_LINE_HEIGHT = 16;
+const X_LABEL_OFFSET = 15;
interface State {
leakLegendTextWidth?: number;
.nice();
};
+ isYScaleLinear(yScale: YScale): yScale is ScaleLinear<number, number> {
+ return 'ticks' in yScale;
+ }
+
getXScale = ({ startDate, endDate }: Props, availableWidth: number, flatData: Chart.Point[]) => {
const dateRange = extent(flatData, (d) => d.x) as [Date, Date];
const start = startDate && startDate > dateRange[0] ? startDate : dateRange[0];
renderHorizontalGrid = () => {
const { formatYTick } = this.props;
const { xScale, yScale } = this.state;
- const hasTicks = typeof yScale.ticks === 'function';
+ const hasTicks = this.isYScaleLinear(yScale);
+
let ticks: Array<string | number> = hasTicks
? yScale.ticks(this.props.maxYTicksCount)
: yScale.domain();
return (
<g>
- {ticks.map((tick) => (
- <g key={tick}>
- {formatYTick != null && (
- <text
- className="line-chart-tick line-chart-tick-x"
- dx="-1em"
- dy="0.3em"
- textAnchor="end"
- x={xScale.range()[0]}
- y={yScale(tick)}
- >
- {formatYTick(tick)}
- </text>
- )}
- <line
- className="line-chart-grid"
- x1={xScale.range()[0]}
- x2={xScale.range()[1]}
- y1={yScale(tick)}
- y2={yScale(tick)}
- />
- </g>
- ))}
+ {ticks.map((tick) => {
+ const y = yScale(tick as YPoint);
+ return (
+ <g key={tick}>
+ {formatYTick != null && (
+ <text
+ className="line-chart-tick line-chart-tick-x"
+ dx="-1em"
+ dy="0.3em"
+ textAnchor="end"
+ x={xScale.range()[0]}
+ y={y}
+ >
+ {formatYTick(tick)}
+ </text>
+ )}
+ <line
+ className="line-chart-grid"
+ x1={xScale.range()[0]}
+ x2={xScale.range()[1]}
+ y1={y}
+ y2={y}
+ />
+ </g>
+ );
+ })}
</g>
);
};
return (
<g transform="translate(0, 20)">
{ticks.slice(0, -1).map((tick, index) => {
- const nextTick = index + 1 < ticks.length ? ticks[index + 1] : xScale.domain()[1];
- const x = (xScale(tick) + xScale(nextTick)) / 2;
+ const x = xScale(tick);
return (
<text
className="line-chart-tick"
// eslint-disable-next-line react/no-array-index-key
key={index}
textAnchor="end"
- transform={`rotate(-35, ${x}, ${y})`}
- x={x}
+ transform={`rotate(-35, ${x + X_LABEL_OFFSET}, ${y})`}
+ x={x + X_LABEL_OFFSET}
y={y}
>
{format(tick)}
const yRange = yScale.range();
const xRange = xScale.range();
- const legendMinWidth = (leakLegendTextWidth || 0) + rawSizes.grid;
+ const legendMinWidth = (leakLegendTextWidth ?? 0) + rawSizes.grid;
const legendPadding = rawSizes.grid / 2;
let legendBackgroundPosition;
};
renderLines = () => {
+ const { xScale, yScale } = this.state;
+
const lineGenerator = d3Line<Chart.Point>()
.defined((d) => Boolean(d.y || d.y === 0))
- .x((d) => this.state.xScale(d.x))
- .y((d) => this.state.yScale(d.y));
+ .x((d) => xScale(d.x))
+ .y((d) => yScale(d.y as YPoint) as number);
+
if (this.props.basisCurve) {
lineGenerator.curve(curveBasis);
}
<g>
{this.props.series.map((serie, idx) => (
<path
- className={classNames('line-chart-path', 'line-chart-path-' + idx)}
- d={lineGenerator(serie.data) || undefined}
+ className={classNames('line-chart-path', `line-chart-path-${idx}`)}
+ d={lineGenerator(serie.data) ?? undefined}
key={serie.name}
/>
))}
};
renderDots = () => {
+ const { series } = this.props;
+ const { xScale, yScale } = this.state;
return (
<g>
- {this.props.series
+ {series
.map((serie, serieIdx) =>
serie.data
.map((point, idx) => {
}
return (
<circle
- className={classNames('line-chart-dot', 'line-chart-dot-' + serieIdx)}
- cx={this.state.xScale(point.x)}
- cy={this.state.yScale(point.y)}
- // eslint-disable-next-line react/no-array-index-key
- key={serie.name + idx}
+ className={classNames('line-chart-dot', `line-chart-dot-${serieIdx}`)}
+ cx={xScale(point.x)}
+ cy={yScale(point.y as YPoint)}
+ key={`${serie.name}${idx}`}
r="2"
/>
);
};
renderAreas = () => {
+ const { series, basisCurve } = this.props;
+ const { xScale, yScale } = this.state;
+
const areaGenerator = area<Chart.Point>()
.defined((d) => Boolean(d.y || d.y === 0))
- .x((d) => this.state.xScale(d.x))
- .y1((d) => this.state.yScale(d.y))
- .y0(this.state.yScale(0));
- if (this.props.basisCurve) {
+ .x((d) => xScale(d.x))
+ .y1((d) => yScale(d.y as YPoint) as number)
+ .y0(yScale(0) as number);
+
+ if (basisCurve) {
areaGenerator.curve(curveBasis);
}
return (
<g>
- {this.props.series.map((serie, idx) => (
+ {series.map((serie, idx) => (
<path
- className={classNames('line-chart-area', 'line-chart-area-' + idx)}
- d={areaGenerator(serie.data) || undefined}
+ className={classNames('line-chart-area', `line-chart-area-${idx}`)}
+ d={areaGenerator(serie.data) ?? undefined}
key={serie.name}
/>
))}
}
return (
<circle
- className={classNames('line-chart-dot', 'line-chart-dot-' + idx)}
+ className={classNames('line-chart-dot', `line-chart-dot-${idx}`)}
cx={selectedDateXPos}
- cy={yScale(point.y)}
+ cy={yScale(point.y as YPoint)}
key={serie.name}
r="4"
/>
};
renderClipPath = () => {
+ const { yScale, xScale } = this.state;
+
return (
<defs>
<clipPath id="chart-clip">
<rect
- height={this.state.yScale.range()[0] + 10}
+ height={yScale.range()[0] + 10}
transform="translate(0,-5)"
- width={this.state.xScale.range()[1]}
+ width={xScale.range()[1]}
/>
</clipPath>
</defs>
};
renderMouseEventsOverlay = (zoomEnabled: boolean) => {
+ const { yScale, xScale } = this.state;
+
const mouseEvents: Partial<React.SVGProps<SVGRectElement>> = {};
if (zoomEnabled) {
mouseEvents.onWheel = this.handleWheel;
return (
<rect
className="chart-mouse-events-overlay"
- height={this.state.yScale.range()[0]}
- width={this.state.xScale.range()[1]}
+ height={yScale.range()[0]}
+ width={xScale.range()[1]}
{...mouseEvents}
/>
);
return <div />;
}
const zoomEnabled = !disableZoom && this.props.updateZoom != null;
- const isZoomed = Boolean(startDate || endDate);
+ const isZoomed = Boolean(startDate ?? endDate);
return (
<svg
aria-label={graphDescription}
className="line-chart-tick"
key="0"
textAnchor="end"
- transform="rotate(-35, 1.875, 24)"
- x={1.875}
+ transform="rotate(-35, 15, 24)"
+ x={15}
y={24}
>
October
className="line-chart-tick"
key="1"
textAnchor="end"
- transform="rotate(-35, 5.625, 24)"
- x={5.625}
+ transform="rotate(-35, 18.75, 24)"
+ x={18.75}
y={24}
>
06 AM
className="line-chart-tick"
key="2"
textAnchor="end"
- transform="rotate(-35, 9.375, 24)"
- x={9.375}
+ transform="rotate(-35, 22.5, 24)"
+ x={22.5}
y={24}
>
12 PM
className="line-chart-tick"
key="3"
textAnchor="end"
- transform="rotate(-35, 13.125, 24)"
- x={13.125}
+ transform="rotate(-35, 26.25, 24)"
+ x={26.25}
y={24}
>
06 PM
className="line-chart-tick"
key="4"
textAnchor="end"
- transform="rotate(-35, 16.875, 24)"
- x={16.875}
+ transform="rotate(-35, 30, 24)"
+ x={30}
y={24}
>
Wed 02
className="line-chart-tick"
key="5"
textAnchor="end"
- transform="rotate(-35, 20.625, 24)"
- x={20.625}
+ transform="rotate(-35, 33.75, 24)"
+ x={33.75}
y={24}
>
06 AM
className="line-chart-tick"
key="6"
textAnchor="end"
- transform="rotate(-35, 24.375, 24)"
- x={24.375}
+ transform="rotate(-35, 37.5, 24)"
+ x={37.5}
y={24}
>
12 PM
className="line-chart-tick"
key="7"
textAnchor="end"
- transform="rotate(-35, 28.125, 24)"
- x={28.125}
+ transform="rotate(-35, 41.25, 24)"
+ x={41.25}
y={24}
>
06 PM
className="line-chart-tick"
key="0"
textAnchor="end"
- transform="rotate(-35, 1.875, 24)"
- x={1.875}
+ transform="rotate(-35, 15, 24)"
+ x={15}
y={24}
>
October
className="line-chart-tick"
key="1"
textAnchor="end"
- transform="rotate(-35, 5.625, 24)"
- x={5.625}
+ transform="rotate(-35, 18.75, 24)"
+ x={18.75}
y={24}
>
06 AM
className="line-chart-tick"
key="2"
textAnchor="end"
- transform="rotate(-35, 9.375, 24)"
- x={9.375}
+ transform="rotate(-35, 22.5, 24)"
+ x={22.5}
y={24}
>
12 PM
className="line-chart-tick"
key="3"
textAnchor="end"
- transform="rotate(-35, 13.125, 24)"
- x={13.125}
+ transform="rotate(-35, 26.25, 24)"
+ x={26.25}
y={24}
>
06 PM
className="line-chart-tick"
key="4"
textAnchor="end"
- transform="rotate(-35, 16.875, 24)"
- x={16.875}
+ transform="rotate(-35, 30, 24)"
+ x={30}
y={24}
>
Wed 02
className="line-chart-tick"
key="5"
textAnchor="end"
- transform="rotate(-35, 20.625, 24)"
- x={20.625}
+ transform="rotate(-35, 33.75, 24)"
+ x={33.75}
y={24}
>
06 AM
className="line-chart-tick"
key="6"
textAnchor="end"
- transform="rotate(-35, 24.375, 24)"
- x={24.375}
+ transform="rotate(-35, 37.5, 24)"
+ x={37.5}
y={24}
>
12 PM
className="line-chart-tick"
key="7"
textAnchor="end"
- transform="rotate(-35, 28.125, 24)"
- x={28.125}
+ transform="rotate(-35, 41.25, 24)"
+ x={41.25}
y={24}
>
06 PM
className="line-chart-tick"
key="0"
textAnchor="end"
- transform="rotate(-35, 1.875, 24)"
- x={1.875}
+ transform="rotate(-35, 15, 24)"
+ x={15}
y={24}
>
October
className="line-chart-tick"
key="1"
textAnchor="end"
- transform="rotate(-35, 5.625, 24)"
- x={5.625}
+ transform="rotate(-35, 18.75, 24)"
+ x={18.75}
y={24}
>
06 AM
className="line-chart-tick"
key="2"
textAnchor="end"
- transform="rotate(-35, 9.375, 24)"
- x={9.375}
+ transform="rotate(-35, 22.5, 24)"
+ x={22.5}
y={24}
>
12 PM
className="line-chart-tick"
key="3"
textAnchor="end"
- transform="rotate(-35, 13.125, 24)"
- x={13.125}
+ transform="rotate(-35, 26.25, 24)"
+ x={26.25}
y={24}
>
06 PM
className="line-chart-tick"
key="4"
textAnchor="end"
- transform="rotate(-35, 16.875, 24)"
- x={16.875}
+ transform="rotate(-35, 30, 24)"
+ x={30}
y={24}
>
Wed 02
className="line-chart-tick"
key="5"
textAnchor="end"
- transform="rotate(-35, 20.625, 24)"
- x={20.625}
+ transform="rotate(-35, 33.75, 24)"
+ x={33.75}
y={24}
>
06 AM
className="line-chart-tick"
key="6"
textAnchor="end"
- transform="rotate(-35, 24.375, 24)"
- x={24.375}
+ transform="rotate(-35, 37.5, 24)"
+ x={37.5}
y={24}
>
12 PM
className="line-chart-tick"
key="7"
textAnchor="end"
- transform="rotate(-35, 28.125, 24)"
- x={28.125}
+ transform="rotate(-35, 41.25, 24)"
+ x={41.25}
y={24}
>
06 PM
className="line-chart-tick"
key="0"
textAnchor="end"
- transform="rotate(-35, 1.875, 24)"
- x={1.875}
+ transform="rotate(-35, 15, 24)"
+ x={15}
y={24}
>
October
className="line-chart-tick"
key="1"
textAnchor="end"
- transform="rotate(-35, 5.625, 24)"
- x={5.625}
+ transform="rotate(-35, 18.75, 24)"
+ x={18.75}
y={24}
>
06 AM
className="line-chart-tick"
key="2"
textAnchor="end"
- transform="rotate(-35, 9.375, 24)"
- x={9.375}
+ transform="rotate(-35, 22.5, 24)"
+ x={22.5}
y={24}
>
12 PM
className="line-chart-tick"
key="3"
textAnchor="end"
- transform="rotate(-35, 13.125, 24)"
- x={13.125}
+ transform="rotate(-35, 26.25, 24)"
+ x={26.25}
y={24}
>
06 PM
className="line-chart-tick"
key="4"
textAnchor="end"
- transform="rotate(-35, 16.875, 24)"
- x={16.875}
+ transform="rotate(-35, 30, 24)"
+ x={30}
y={24}
>
Wed 02
className="line-chart-tick"
key="5"
textAnchor="end"
- transform="rotate(-35, 20.625, 24)"
- x={20.625}
+ transform="rotate(-35, 33.75, 24)"
+ x={33.75}
y={24}
>
06 AM
className="line-chart-tick"
key="6"
textAnchor="end"
- transform="rotate(-35, 24.375, 24)"
- x={24.375}
+ transform="rotate(-35, 37.5, 24)"
+ x={37.5}
y={24}
>
12 PM
className="line-chart-tick"
key="7"
textAnchor="end"
- transform="rotate(-35, 28.125, 24)"
- x={28.125}
+ transform="rotate(-35, 41.25, 24)"
+ x={41.25}
y={24}
>
06 PM
className="line-chart-tick"
key="0"
textAnchor="end"
- transform="rotate(-35, 1.875, 24)"
- x={1.875}
+ transform="rotate(-35, 15, 24)"
+ x={15}
y={24}
>
October
className="line-chart-tick"
key="1"
textAnchor="end"
- transform="rotate(-35, 5.625, 24)"
- x={5.625}
+ transform="rotate(-35, 18.75, 24)"
+ x={18.75}
y={24}
>
06 AM
className="line-chart-tick"
key="2"
textAnchor="end"
- transform="rotate(-35, 9.375, 24)"
- x={9.375}
+ transform="rotate(-35, 22.5, 24)"
+ x={22.5}
y={24}
>
12 PM
className="line-chart-tick"
key="3"
textAnchor="end"
- transform="rotate(-35, 13.125, 24)"
- x={13.125}
+ transform="rotate(-35, 26.25, 24)"
+ x={26.25}
y={24}
>
06 PM
className="line-chart-tick"
key="4"
textAnchor="end"
- transform="rotate(-35, 16.875, 24)"
- x={16.875}
+ transform="rotate(-35, 30, 24)"
+ x={30}
y={24}
>
Wed 02
className="line-chart-tick"
key="5"
textAnchor="end"
- transform="rotate(-35, 20.625, 24)"
- x={20.625}
+ transform="rotate(-35, 33.75, 24)"
+ x={33.75}
y={24}
>
06 AM
className="line-chart-tick"
key="6"
textAnchor="end"
- transform="rotate(-35, 24.375, 24)"
- x={24.375}
+ transform="rotate(-35, 37.5, 24)"
+ x={37.5}
y={24}
>
12 PM
className="line-chart-tick"
key="7"
textAnchor="end"
- transform="rotate(-35, 28.125, 24)"
- x={28.125}
+ transform="rotate(-35, 41.25, 24)"
+ x={41.25}
y={24}
>
06 PM