blob: 867dd52e3bdb30b8c654527e54f28f7a5bf6e036 [file] [log] [blame]
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
/* eslint-disable react/sort-prop-types, react/jsx-sort-default-props */
import React from 'react';
import PropTypes from 'prop-types';
import { ParentSize } from '@vx/responsive';
const propTypes = {
className: PropTypes.string,
width: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),
height: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),
renderChart: PropTypes.func.isRequired,
renderLegend: PropTypes.func.isRequired,
position: PropTypes.oneOf(['top', 'left', 'bottom', 'right']),
legendJustifyContent: PropTypes.oneOf(['center', 'flex-start', 'flex-end']),
};
const defaultProps = {
className: '',
width: 'auto',
height: 'auto',
position: 'top',
legendJustifyContent: undefined,
};
const LEGEND_STYLE_BASE = {
display: 'flex',
flexGrow: 0,
flexShrink: 0,
order: -1,
paddingTop: '5px',
fontSize: '0.9em',
};
const CHART_STYLE_BASE = {
flexGrow: 1,
flexShrink: 1,
flexBasis: 'auto',
position: 'relative',
};
class WithLegend extends React.Component {
getContainerDirection() {
const { position } = this.props;
switch (position) {
case 'left':
return 'row';
case 'right':
return 'row-reverse';
case 'bottom':
return 'column-reverse';
default:
case 'top':
return 'column';
}
}
getLegendJustifyContent() {
const { legendJustifyContent, position } = this.props;
if (legendJustifyContent) {
return legendJustifyContent;
}
switch (position) {
case 'left':
return 'flex-start';
case 'right':
return 'flex-start';
case 'bottom':
return 'flex-end';
default:
case 'top':
return 'flex-end';
}
}
render() {
const { className, width, height, position, renderChart, renderLegend } = this.props;
const isHorizontal = position === 'left' || position === 'right';
const style = {
display: 'flex',
flexDirection: this.getContainerDirection(),
};
if (width) {
style.width = width;
}
if (height) {
style.height = height;
}
const chartStyle = { ...CHART_STYLE_BASE };
if (isHorizontal) {
chartStyle.width = 0;
} else {
chartStyle.height = 0;
}
const legendDirection = isHorizontal ? 'column' : 'row';
const legendStyle = {
...LEGEND_STYLE_BASE,
flexDirection: legendDirection,
justifyContent: this.getLegendJustifyContent(),
};
const legendContainerStyle = {
flexWrap: 'wrap',
display: 'flex',
flexDirection: legendDirection,
};
return (
<div className={`with-legend ${className}`} style={style}>
<div className="legend-container" style={legendStyle}>
{renderLegend({
// Pass flexDirection for @vx/legend to arrange legend items
direction: legendDirection,
style: legendContainerStyle,
})}
</div>
<div className="main-container" style={chartStyle}>
<ParentSize>
{parent =>
parent.width > 0 && parent.height > 0
? // Only render when necessary
renderChart(parent)
: null
}
</ParentSize>
</div>
</div>
);
}
}
WithLegend.propTypes = propTypes;
WithLegend.defaultProps = defaultProps;
export default WithLegend;