848 lines
23 KiB
TypeScript
848 lines
23 KiB
TypeScript
import React from "react";
|
|
import { Range } from "common/RangeInput";
|
|
import moment from "moment";
|
|
import AutoSizer from "react-virtualized-auto-sizer";
|
|
import { abbreviateNum } from "utils/numbers";
|
|
import { or } from "utils/types";
|
|
import {
|
|
createContainer,
|
|
ScalePropType,
|
|
VictoryArea,
|
|
VictoryAxis,
|
|
VictoryChart,
|
|
VictoryLabel,
|
|
VictoryLegend,
|
|
VictoryLine,
|
|
VictoryPie,
|
|
VictoryScatter,
|
|
VictoryTheme,
|
|
VictoryTooltip,
|
|
VictoryTooltipProps
|
|
} from "victory";
|
|
import {
|
|
ResponsiveContainer,
|
|
RadarChart,
|
|
PolarGrid,
|
|
PolarAngleAxis,
|
|
PolarRadiusAxis,
|
|
Radar,
|
|
Legend
|
|
} from "recharts";
|
|
import { pond } from "protobuf-ts/pond";
|
|
import { getTextPrimary, getTextSecondary } from "theme/text";
|
|
import { darken, useTheme } from "@mui/material";
|
|
import { useWidth } from "hooks";
|
|
import { useNavigate } from "react-router-dom";
|
|
const VictoryCursorVoronoiContainer: any = createContainer("cursor", "voronoi");
|
|
|
|
export enum GraphOrientation {
|
|
GRAPH_ORIENTATION_PORTRAIT = 0,
|
|
GRAPH_ORIENTATION_LANDSCAPE = 1
|
|
}
|
|
|
|
export enum GraphType {
|
|
LINEAR = 0,
|
|
AREA = 1,
|
|
BAR = 2,
|
|
MULTILINE = 3,
|
|
SCATTER = 4,
|
|
PIE = 5,
|
|
RADAR = 6
|
|
}
|
|
|
|
export interface vXAxis {
|
|
scale: ScalePropType;
|
|
label: string;
|
|
}
|
|
|
|
export interface GraphPoint {
|
|
x: any;
|
|
y: any;
|
|
y0?: any;
|
|
bubble?: number;
|
|
colour?: string;
|
|
}
|
|
|
|
export interface PieSlice {
|
|
x: string;
|
|
y: number;
|
|
fill: string;
|
|
linkToOnClick?: string;
|
|
}
|
|
|
|
export interface InteractionLine {
|
|
value: any;
|
|
colour: any;
|
|
}
|
|
|
|
export interface GraphComponent {
|
|
label: string;
|
|
data: Array<GraphPoint> | Array<Array<GraphPoint>> | Array<PieSlice>;
|
|
overlays?: pond.ComponentOverlays[];
|
|
tick?: string;
|
|
colour: any;
|
|
type: GraphType;
|
|
range?: Range;
|
|
interactionLines?: Array<InteractionLine>;
|
|
isBoolean?: boolean;
|
|
states?: Array<string>;
|
|
isPercent?: boolean;
|
|
xAxis?: vXAxis;
|
|
decimals?: number;
|
|
horizontalBars?: boolean;
|
|
}
|
|
|
|
interface CustomVictoryTooltipProps extends VictoryTooltipProps {
|
|
height: any;
|
|
width: any;
|
|
}
|
|
|
|
class CustomVictoryTooltip extends React.Component<CustomVictoryTooltipProps> {
|
|
render() {
|
|
const { x, height, width, text } = this.props;
|
|
let y = height / 2;
|
|
let orientation: "right" | "left" = x && x < width * 0.66 ? "right" : "left";
|
|
let xOffset: number = orientation === "right" ? 5 : -5;
|
|
return (
|
|
<VictoryTooltip
|
|
active
|
|
x={x}
|
|
y={y}
|
|
dx={xOffset}
|
|
orientation={orientation}
|
|
pointerLength={0}
|
|
flyoutStyle={{ stroke: "none", strokeWidth: 0, fill: "#323232" }}
|
|
style={{ fill: "#fff" }}
|
|
text={text}
|
|
/>
|
|
);
|
|
}
|
|
}
|
|
|
|
interface Props /*extends WithWidth, RouteComponentProps, WithTheme*/ {
|
|
orientation?: GraphOrientation;
|
|
graphComponent?: GraphComponent;
|
|
}
|
|
|
|
interface State {
|
|
interactionComponents: Array<any>;
|
|
dataComponent: any;
|
|
updateCounter: number;
|
|
overlayComponents: Array<any>;
|
|
}
|
|
|
|
//a linear graph with the option for two y-axis
|
|
class Graph extends React.Component<Props, State> {
|
|
constructor(props: Props) {
|
|
super(props);
|
|
this.state = {
|
|
interactionComponents: [],
|
|
dataComponent: null,
|
|
updateCounter: 0,
|
|
overlayComponents: []
|
|
};
|
|
}
|
|
|
|
componentDidMount = () => {
|
|
this.setupGraph();
|
|
};
|
|
|
|
componentDidUpdate = (prevProps: Props) => {
|
|
if (prevProps.graphComponent !== this.props.graphComponent) {
|
|
this.setupGraph();
|
|
this.setState(prevState => ({
|
|
updateCounter: prevState.updateCounter + 1
|
|
}));
|
|
}
|
|
};
|
|
|
|
setupGraph = () => {
|
|
const { graphComponent } = this.props;
|
|
if (graphComponent && or(graphComponent.data, []).length > 0) {
|
|
let dataComponent = this.getDataComponent(graphComponent, "graphComponent");
|
|
switch (graphComponent.type) {
|
|
case GraphType.PIE:
|
|
this.setState({
|
|
interactionComponents: [],
|
|
dataComponent: dataComponent
|
|
});
|
|
break;
|
|
case GraphType.RADAR:
|
|
this.setState({
|
|
interactionComponents: [],
|
|
dataComponent: dataComponent
|
|
});
|
|
break;
|
|
default:
|
|
let timeData, startDate: string, endDate: string;
|
|
timeData =
|
|
graphComponent.type === GraphType.MULTILINE
|
|
? (graphComponent.data[0] as Array<GraphPoint>)
|
|
: (graphComponent.data as Array<GraphPoint>);
|
|
|
|
startDate = timeData[timeData.length - 1].x;
|
|
endDate = timeData[0].x;
|
|
timeData.forEach((d: GraphPoint) => {
|
|
if (moment(d.x).isBefore(moment(startDate))) {
|
|
startDate = d.x;
|
|
}
|
|
if (moment(d.x).isAfter(moment(endDate))) {
|
|
endDate = d.x;
|
|
}
|
|
});
|
|
|
|
this.setState({
|
|
interactionComponents: this.getInteractionComponents(
|
|
graphComponent.interactionLines ? graphComponent.interactionLines : [],
|
|
startDate,
|
|
endDate
|
|
),
|
|
dataComponent: dataComponent,
|
|
overlayComponents: this.getOverlayComponents(startDate, endDate)
|
|
});
|
|
|
|
break;
|
|
}
|
|
}
|
|
};
|
|
|
|
getLegend = () => {
|
|
const { graphComponent } = this.props;
|
|
const textColour = getTextSecondary()
|
|
|
|
if (!graphComponent) {
|
|
return [];
|
|
}
|
|
//prepares the initial object
|
|
var legend = [
|
|
{
|
|
name: graphComponent.label,
|
|
symbol: { fill: graphComponent.colour },
|
|
labels: { fill: textColour }
|
|
}
|
|
];
|
|
|
|
return legend;
|
|
};
|
|
|
|
//uses the material theme from victory as a base
|
|
customTheme = () => {
|
|
let graphTheme = VictoryTheme.material as any; //need to cast as 'any' because Victory has an issue with their ThemeInterface
|
|
|
|
//removing the grid
|
|
graphTheme.axis.style.grid = {
|
|
fill: "none",
|
|
stroke: "none"
|
|
};
|
|
|
|
graphTheme.voronoi.style.flyout = {
|
|
...graphTheme.voronoi.style.flyout,
|
|
...{ fill: "#323232", stroke: "none", strokeWidth: 0 }
|
|
};
|
|
|
|
graphTheme.voronoi.style.labels = {
|
|
...graphTheme.voronoi.style.labels,
|
|
...{ fill: "#fff" }
|
|
};
|
|
|
|
return graphTheme;
|
|
};
|
|
|
|
getInteractionData = (interactionLine: InteractionLine, startDate: any, endDate: any) => {
|
|
return [
|
|
{ x: startDate, y: interactionLine.value },
|
|
{ x: endDate, y: interactionLine.value }
|
|
];
|
|
};
|
|
|
|
getInteractionComponents = (
|
|
interactionLines: Array<InteractionLine>,
|
|
startDate: any,
|
|
endDate: any
|
|
) => {
|
|
var interactionComponents = [];
|
|
for (let i = 0; i < or(interactionLines, []).length; i++) {
|
|
interactionComponents.push(
|
|
<VictoryLine
|
|
key={i}
|
|
name={"interactionLine-" + i.toString()}
|
|
style={{
|
|
data: {
|
|
stroke: interactionLines[i].colour,
|
|
strokeOpacity: 0.3,
|
|
strokeDasharray: "5, 5"
|
|
}
|
|
}}
|
|
data={this.getInteractionData(interactionLines[i], startDate, endDate)}
|
|
x="x"
|
|
y="y"
|
|
/>
|
|
);
|
|
}
|
|
|
|
return interactionComponents;
|
|
};
|
|
|
|
getOverlayData = (overlay: pond.ComponentOverlays, startDate: any, endDate: any) => {
|
|
return [
|
|
{ x: startDate, y: overlay.max, y0: overlay.min },
|
|
{ x: endDate, y: overlay.max, y0: overlay.min }
|
|
];
|
|
};
|
|
|
|
getOverlayComponents = (startDate: any, endDate: any) => {
|
|
var overlayComponents: JSX.Element[] = [];
|
|
if (this.props.graphComponent?.overlays) {
|
|
let o = this.props.graphComponent?.overlays;
|
|
o.forEach((overlay, i) => {
|
|
overlayComponents.push(
|
|
<VictoryArea
|
|
key={"overlay" + i}
|
|
style={{
|
|
data: {
|
|
fill: overlay.colour,
|
|
opacity: 0.15
|
|
}
|
|
}}
|
|
data={this.getOverlayData(overlay, startDate, endDate)}
|
|
x="x"
|
|
y="y"
|
|
/>
|
|
);
|
|
});
|
|
}
|
|
return overlayComponents;
|
|
};
|
|
|
|
getAreaComponent = (data: Array<GraphPoint>, colour: any, key: string) => {
|
|
return (
|
|
<VictoryArea
|
|
key={key}
|
|
style={{
|
|
data: { fill: colour, fillOpacity: 0.33 }
|
|
}}
|
|
data={data}
|
|
x="x"
|
|
y="y"
|
|
interpolation="monotoneX"
|
|
/>
|
|
);
|
|
};
|
|
|
|
// getBarComponent = (
|
|
// data: Array<GraphPoint>,
|
|
// colour: any,
|
|
// key: string,
|
|
// horizontal: boolean = false
|
|
// ) => {
|
|
// return (
|
|
// <VictoryBar
|
|
// key={key}
|
|
// horizontal={horizontal}
|
|
// style={{
|
|
// data: {
|
|
// fill: (d: GraphPoint) => or(d.colour, colour),
|
|
// stroke: (d: GraphPoint) => or(d.colour, colour),
|
|
// fillOpacity: 0.6,
|
|
// strokeWidth: 1
|
|
// }
|
|
// }}
|
|
// alignment="start"
|
|
// barWidth={7}
|
|
// cornerRadius={0}
|
|
// data={data}
|
|
// x="x"
|
|
// y="y"
|
|
// />
|
|
// );
|
|
// };
|
|
|
|
//data should contain an array of arrays (lines)
|
|
getMultilineComponent = (data: Array<Array<GraphPoint>>, colour: any, key: string) => {
|
|
let lines = [];
|
|
for (var i = 0; i < or(data, []).length; i++) {
|
|
lines.push(this.getLineComponent(data[i], colour, key + "-" + i.toString()));
|
|
}
|
|
return lines;
|
|
};
|
|
|
|
getScatterComponent = (data: Array<GraphPoint>, colour: any, key: string) => {
|
|
// const { theme } = this.props;
|
|
return (
|
|
<VictoryScatter
|
|
key={key}
|
|
maxBubbleSize={50}
|
|
size={datum => datum.bubble * 1.5 + 5}
|
|
style={{
|
|
data: {
|
|
fill: colour,
|
|
fillOpacity: (datum: any) => (datum.bubble > 0 ? 0.65 : 0.25),
|
|
stroke: darken(colour, 0.25),
|
|
strokeOpacity: (datum: any) => (datum.bubble > 0 ? 0.65 : 0.25),
|
|
strokeWidth: 1
|
|
},
|
|
labels: {
|
|
fill: getTextPrimary(),
|
|
fontSize: 10
|
|
}
|
|
}}
|
|
labels={(datum: any) => (datum.bubble > 0 ? datum.bubble : "")}
|
|
labelComponent={<VictoryLabel dy={13} />}
|
|
data={data}
|
|
x="x"
|
|
y="y"
|
|
/>
|
|
);
|
|
};
|
|
|
|
getRadarComponent = (data: Array<GraphPoint>, colour: any, key: string) => {
|
|
let dataTemplate = this.getWindDirTemplate();
|
|
return (
|
|
<RadarChart data={this.assembleRadarData(data, dataTemplate)} key={key}>
|
|
<Legend />
|
|
<PolarGrid />
|
|
<PolarAngleAxis tick={{ fill: colour }} dataKey="key" />
|
|
<PolarRadiusAxis />
|
|
<Radar
|
|
name="Wind Direction"
|
|
dataKey="value"
|
|
stroke={colour}
|
|
fill={colour}
|
|
fillOpacity={0.6}
|
|
/>
|
|
</RadarChart>
|
|
);
|
|
};
|
|
|
|
assembleRadarData = (
|
|
data: Array<GraphPoint>,
|
|
dataTemplate: Array<{ key: string; value: number }>
|
|
) => {
|
|
let aData = dataTemplate;
|
|
data.forEach(point => {
|
|
if (point.y !== 0) {
|
|
aData[point.y - 1].value = aData[point.y - 1].value + 1;
|
|
}
|
|
});
|
|
return aData;
|
|
};
|
|
|
|
getWindDirTemplate = () => {
|
|
return [
|
|
{
|
|
key: "N",
|
|
value: 0
|
|
},
|
|
{
|
|
key: "NNE",
|
|
value: 0
|
|
},
|
|
{
|
|
key: "NE",
|
|
value: 0
|
|
},
|
|
{
|
|
key: "ENE",
|
|
value: 0
|
|
},
|
|
{
|
|
key: "E",
|
|
value: 0
|
|
},
|
|
{
|
|
key: "ESE",
|
|
value: 0
|
|
},
|
|
{
|
|
key: "SE",
|
|
value: 0
|
|
},
|
|
{
|
|
key: "SSE",
|
|
value: 0
|
|
},
|
|
{
|
|
key: "S",
|
|
value: 0
|
|
},
|
|
{
|
|
key: "SSW",
|
|
value: 0
|
|
},
|
|
{
|
|
key: "SW",
|
|
value: 0
|
|
},
|
|
{
|
|
key: "WSW",
|
|
value: 0
|
|
},
|
|
{
|
|
key: "W",
|
|
value: 0
|
|
},
|
|
{
|
|
key: "WNW",
|
|
value: 0
|
|
},
|
|
{
|
|
key: "NW",
|
|
value: 0
|
|
},
|
|
{
|
|
key: "NNW",
|
|
value: 0
|
|
}
|
|
];
|
|
};
|
|
|
|
getLineComponent = (data: Array<GraphPoint>, colour: any, key: string) => {
|
|
return (
|
|
<VictoryLine
|
|
key={key}
|
|
style={{
|
|
data: { stroke: colour }
|
|
}}
|
|
data={data}
|
|
x="x"
|
|
y="y"
|
|
interpolation="monotoneX"
|
|
/>
|
|
);
|
|
};
|
|
|
|
//dynamically loads a data component based on type (area, bar, line), defaults to line*/
|
|
getDataComponent(graphComponent: GraphComponent, key: string): any {
|
|
const { type, data, colour } = graphComponent;
|
|
switch (type) {
|
|
case GraphType.AREA:
|
|
return this.getAreaComponent(data as Array<GraphPoint>, colour, key);
|
|
// case GraphType.BAR:
|
|
// return this.getBarComponent(
|
|
// data as Array<GraphPoint>,
|
|
// colour,
|
|
// key,
|
|
// graphComponent.horizontalBars
|
|
// );
|
|
case GraphType.MULTILINE:
|
|
return this.getMultilineComponent(data as Array<Array<GraphPoint>>, colour, key);
|
|
case GraphType.SCATTER:
|
|
return this.getScatterComponent(data as Array<GraphPoint>, colour, key);
|
|
case GraphType.PIE:
|
|
return;
|
|
case GraphType.RADAR:
|
|
return this.getRadarComponent(data as Array<GraphPoint>, colour, key);
|
|
default:
|
|
//line component is default
|
|
return this.getLineComponent(data as Array<GraphPoint>, colour, key);
|
|
}
|
|
}
|
|
|
|
getContainerHeight = () => {
|
|
const { orientation } = this.props;
|
|
const width = useWidth()
|
|
if (orientation === GraphOrientation.GRAPH_ORIENTATION_LANDSCAPE) {
|
|
switch (width) {
|
|
case "xs":
|
|
return 250;
|
|
case "sm":
|
|
return 300;
|
|
case "md":
|
|
return 350;
|
|
case "lg":
|
|
return 375;
|
|
case "xl":
|
|
return 400;
|
|
default:
|
|
return 300;
|
|
}
|
|
} else {
|
|
//default portrait view
|
|
return 300;
|
|
}
|
|
};
|
|
|
|
emptyPieSlice(): PieSlice {
|
|
// const { theme } = this.props;
|
|
const theme = useTheme()
|
|
return {
|
|
x: "",
|
|
y: 1,
|
|
fill: theme.palette.grey.A700
|
|
} as PieSlice;
|
|
}
|
|
|
|
// fillCallback: VictoryStringOrNumberCallback = (d: PieSlice) => {
|
|
// return d.fill
|
|
// }
|
|
|
|
getPieChart(width: any, height: any) {
|
|
const { graphComponent } = this.props;
|
|
const theme = useTheme()
|
|
const navigate = useNavigate()
|
|
const { data } = graphComponent ? graphComponent : { data: [] };
|
|
const { label } = graphComponent ? graphComponent : { label: "" };
|
|
const legendData = (data as PieSlice[]).map(d => {
|
|
return {
|
|
name: d.x + " (" + d.y + ")",
|
|
symbol: { fill: d.fill },
|
|
labels: { fill: theme.palette.text.primary, fontSize: width >= 300 ? 16 : 14 }
|
|
};
|
|
});
|
|
|
|
//filter out zero-slices
|
|
const filteredData: PieSlice[] = (data as PieSlice[]).filter(d => d.y > 0);
|
|
let hasActions = false;
|
|
filteredData.forEach(d => {
|
|
if (d.linkToOnClick) hasActions = true;
|
|
});
|
|
|
|
//pie chart is empty
|
|
if (filteredData.length <= 0) {
|
|
filteredData.push(this.emptyPieSlice());
|
|
}
|
|
|
|
return (
|
|
<svg width={width} height={height}>
|
|
<VictoryLegend
|
|
standalone={false}
|
|
gutter={10}
|
|
orientation="horizontal"
|
|
itemsPerRow={width >= 600 ? 3 : width >= 300 ? 2 : 1}
|
|
data={legendData}
|
|
/>
|
|
<VictoryPie
|
|
standalone={false}
|
|
data={filteredData}
|
|
x="x"
|
|
y="y"
|
|
style={{
|
|
data: {
|
|
// fill: (d: PieSlice): VictoryStringOrNumberCallback => {
|
|
// return (d.fill)
|
|
// },
|
|
cursor: hasActions ? "pointer" : "default"
|
|
},
|
|
labels: {
|
|
fontSize: "16px",
|
|
fill: theme.palette.text.primary
|
|
}
|
|
}}
|
|
height={height}
|
|
width={width}
|
|
padAngle={2}
|
|
innerRadius={height * 0.25}
|
|
padding={{
|
|
top: height * 0.25,
|
|
right: 0,
|
|
bottom: height * 0.1,
|
|
left: 0
|
|
}}
|
|
labels={() => ""}
|
|
events={[
|
|
{
|
|
target: "data",
|
|
eventHandlers: {
|
|
onClick: () => ({
|
|
mutation: (props: any) => {
|
|
// const { history } = this.props;
|
|
const { datum } = props;
|
|
|
|
if (datum.linkToOnClick) {
|
|
navigate(datum.linkToOnClick);
|
|
}
|
|
}
|
|
}),
|
|
onMouseOver: () => ({
|
|
mutation: props => ({
|
|
radius: hasActions ? props.radius * 1.05 : props.radius
|
|
})
|
|
}),
|
|
onMouseOut: () => ({
|
|
mutation: () => null
|
|
})
|
|
}
|
|
}
|
|
]}
|
|
/>
|
|
{label && label !== "" && (
|
|
<VictoryLabel
|
|
textAnchor="middle"
|
|
style={{ fontSize: 24, fill: theme.palette.text.primary }}
|
|
x={width / 2}
|
|
y={(height / 2) * 1.15}
|
|
text={label}
|
|
/>
|
|
)}
|
|
</svg>
|
|
);
|
|
}
|
|
|
|
render() {
|
|
const theme = useTheme()
|
|
const { graphComponent } = this.props;
|
|
const { interactionComponents, overlayComponents, dataComponent, updateCounter } = this.state;
|
|
|
|
if (!graphComponent || !graphComponent.data) {
|
|
return null;
|
|
}
|
|
|
|
const { range, isBoolean, isPercent } = graphComponent;
|
|
const textColour = theme.palette.text.secondary;
|
|
const legend = this.getLegend();
|
|
const height = this.getContainerHeight();
|
|
|
|
const hideCursor: boolean = [GraphType.BAR].includes(graphComponent.type);
|
|
const disableCursorVoronoi: boolean = [GraphType.AREA, GraphType.MULTILINE].includes(
|
|
graphComponent.type
|
|
);
|
|
|
|
return (
|
|
<AutoSizer disableHeight key={updateCounter}>
|
|
{({ width }: any) => (
|
|
<React.Fragment>
|
|
{/* Don't need the entire graph for pie charts */}
|
|
{graphComponent.type !== GraphType.PIE ? (
|
|
<React.Fragment>
|
|
{/*
|
|
// @ts-ignore */}
|
|
{graphComponent.type === GraphType.RADAR ? (
|
|
<ResponsiveContainer minHeight={450} width={width}>
|
|
{dataComponent}
|
|
</ResponsiveContainer>
|
|
) : (
|
|
<VictoryChart
|
|
theme={this.customTheme()}
|
|
containerComponent={
|
|
<VictoryCursorVoronoiContainer
|
|
voronoiDimension="x"
|
|
cursorDimension="x"
|
|
disable={width === "xs" || disableCursorVoronoi}
|
|
responsive={false}
|
|
cursorComponent={
|
|
hideCursor ? (
|
|
<React.Fragment />
|
|
) : (
|
|
<line
|
|
style={{
|
|
stroke: "#323232",
|
|
strokeWidth: 2
|
|
}}
|
|
/>
|
|
)
|
|
}
|
|
labels={(datum: any) => {
|
|
if (
|
|
graphComponent &&
|
|
or<vXAxis>(graphComponent.xAxis, { scale: "time" } as vXAxis).scale ===
|
|
"time"
|
|
) {
|
|
let decimals = graphComponent.decimals ? graphComponent.decimals : 0;
|
|
return `${moment(or(datum.x, undefined)).calendar()}, ${datum.y.toFixed(
|
|
decimals
|
|
)}`;
|
|
}
|
|
return datum.y;
|
|
}}
|
|
labelComponent={
|
|
hideCursor ? (
|
|
<VictoryTooltip />
|
|
) : (
|
|
<CustomVictoryTooltip width={width} height={height} />
|
|
)
|
|
}
|
|
style={{ touchAction: "auto" }}
|
|
voronoiBlacklist={["interactionLine"]}
|
|
/>
|
|
}
|
|
animate={{ duration: 1000, easing: "expInOut", onLoad: { duration: 500 } }}
|
|
scale={{
|
|
x: or(graphComponent.xAxis, { scale: "time" }).scale,
|
|
y: "linear"
|
|
}}
|
|
width={width}
|
|
height={height}
|
|
padding={{ top: 30, bottom: 30, left: 60, right: 60 }}
|
|
// @ts-ignore */}
|
|
minDomain={{
|
|
y:
|
|
range && range.min !== undefined
|
|
? range.min
|
|
: isBoolean || isPercent
|
|
? 0
|
|
: undefined
|
|
}}
|
|
maxDomain={{
|
|
y:
|
|
range && range.max !== undefined
|
|
? range.max
|
|
: isPercent
|
|
? 100
|
|
: isBoolean
|
|
? 1
|
|
: undefined
|
|
}}
|
|
domainPadding={isBoolean || isPercent ? undefined : { y: 50 }}>
|
|
<VictoryLegend
|
|
x={75}
|
|
centerTitle
|
|
orientation="horizontal"
|
|
style={{ labels: { fontSize: 12 } }}
|
|
data={or(legend, { name: "" } as any)}
|
|
/>
|
|
|
|
{/* X Axis */}
|
|
<VictoryAxis
|
|
crossAxis
|
|
style={{
|
|
axis: { stroke: textColour },
|
|
ticks: { stroke: textColour, fontSize: 7 },
|
|
tickLabels: { fontSize: 12, fill: textColour },
|
|
axisLabel: { fontSize: 12, fill: textColour }
|
|
}}
|
|
standalone={false}
|
|
fixLabelOverlap={true}
|
|
orientation="bottom"
|
|
label={or(graphComponent.xAxis, { label: "" }).label}
|
|
/>
|
|
{/* Y Axis */}
|
|
<VictoryAxis
|
|
crossAxis={false}
|
|
style={{
|
|
axis: { stroke: textColour },
|
|
ticks: { stroke: textColour, fontSize: 4 },
|
|
tickLabels: {
|
|
fontSize: 12,
|
|
padding: 1,
|
|
fill: textColour
|
|
}
|
|
}}
|
|
dependentAxis
|
|
standalone={false}
|
|
fixLabelOverlap={true}
|
|
tickValues={graphComponent.isBoolean ? [0, 1] : undefined}
|
|
tickFormat={(tick: any) =>
|
|
graphComponent.isBoolean
|
|
? tick === 1
|
|
? or(graphComponent.states, ["Off", "On"])[1]
|
|
: or(graphComponent.states, ["Off", "On"])[0]
|
|
: `${abbreviateNum(tick)}` + or(graphComponent.tick, "")
|
|
}
|
|
/>
|
|
{interactionComponents}
|
|
{overlayComponents}
|
|
{dataComponent}
|
|
</VictoryChart>
|
|
)}
|
|
</React.Fragment>
|
|
) : (
|
|
this.getPieChart(width, height)
|
|
)}
|
|
</React.Fragment>
|
|
)}
|
|
</AutoSizer>
|
|
);
|
|
}
|
|
}
|
|
|
|
export default ((Graph));
|