@patternfly/react-charts
Version:
This library provides a set of React chart components for use with the PatternFly reference implementation.
364 lines • 12.8 kB
JavaScript
import { __rest } from "tslib";
import { cloneElement } from 'react';
import defaults from 'lodash/defaults';
import { Helpers, TextSize } from 'victory-core';
import { VictoryLegend } from 'victory-legend';
import { ChartCommonStyles } from '../ChartTheme/ChartStyles';
import { getLabelTextSize } from '../ChartUtils/chart-label';
import { getPieOrigin } from './chart-origin';
/**
* Returns the max text length in a legend data set to calculate the x offset for right aligned legends.
*
* @private Not intended as public API and subject to change
*/
export const getLegendMaxTextWidth = (legendData, theme) => {
let legendXOffset = 0;
legendData.map((data) => {
const labelWidth = getLabelTextSize({ text: data.name, theme }).width;
if (labelWidth > legendXOffset) {
legendXOffset = labelWidth;
}
});
return legendXOffset;
};
/**
* Returns a legend which has been positioned per the given chart properties
*
* @private Not intended as public API and subject to change
*/
export const getComputedLegend = ({ allowWrap = true, chartType = 'chart', colorScale, dx = 0, dy = 0, height, legendComponent, padding, patternScale, position = ChartCommonStyles.legend.position, theme, themeColor, width,
// destructure last
orientation = theme.legend.orientation }) => {
// Get the number of legend items per row
const legendItemsProps = legendComponent.props ? legendComponent.props : {};
const legendItemsPerRow = allowWrap
? getLegendItemsPerRow({
dx,
height,
legendData: legendItemsProps.data,
legendOrientation: legendItemsProps.legendOrientation ? legendItemsProps.legendOrientation : orientation,
legendPosition: position,
legendProps: legendItemsProps,
padding,
theme,
width
})
: undefined;
// Include new itemsPerRow prop when determining x and y position
const legendPositionProps = defaults({}, legendComponent.props, {
itemsPerRow: legendItemsPerRow
});
const legendX = getLegendX({
chartType,
dx,
height,
legendData: legendPositionProps.data,
legendOrientation: legendPositionProps.legendOrientation ? legendPositionProps.legendOrientation : orientation,
legendPosition: position,
legendProps: legendPositionProps,
padding,
theme,
width
});
const legendY = getLegendY({
chartType,
dy,
height,
legendData: legendPositionProps.data,
legendOrientation: legendPositionProps.legendOrientation ? legendPositionProps.legendOrientation : orientation,
legendProps: legendPositionProps,
legendPosition: position,
padding,
theme,
width
});
// Clone legend with updated props
const legendProps = defaults({}, legendComponent.props, {
colorScale,
itemsPerRow: legendItemsPerRow,
orientation,
patternScale,
standalone: false,
theme,
themeColor,
x: legendX > 0 ? legendX : 0,
y: legendY > 0 ? legendY : 0
});
return cloneElement(legendComponent, legendProps);
};
/**
* Returns legend dimensions
*
* @private Not intended as public API and subject to change
*/
export const getLegendDimensions = ({ legendData, legendOrientation, legendProps, theme }) => {
if (legendData || legendProps.data) {
return VictoryLegend.getDimensions(Object.assign({ data: legendData, orientation: legendOrientation, theme }, legendProps // override above
));
}
return {};
};
/**
* Returns true if the legend is smaller than its container
*
* @private Not intended as public API and subject to change
*/
const doesLegendFit = ({ dx = 0, height, legendPosition, legendData, legendOrientation, legendProps, padding, theme, width }) => {
const { left, right } = Helpers.getPadding(padding);
const chartSize = {
height, // Fixed size
width: width - left - right
};
const legendDimensions = getLegendDimensions({
legendData,
legendOrientation,
legendProps,
theme
});
let occupiedWidth = 0;
switch (legendPosition) {
case 'bottom-left':
occupiedWidth = left + dx;
break;
case 'right':
occupiedWidth = chartSize.width + ChartCommonStyles.legend.margin + left + dx;
break;
default:
occupiedWidth = dx;
break;
}
return width - occupiedWidth > legendDimensions.width;
};
/**
* Returns the number of legend items per row
*
* @private Not intended as public API and subject to change
*/
const getLegendItemsPerRow = ({ dx, height, legendPosition, legendData, legendOrientation, legendProps, padding, theme, width }) => {
let itemsPerRow = legendData ? legendData.length : 0;
for (let i = itemsPerRow; i > 0; i--) {
const fits = doesLegendFit({
dx,
height,
legendPosition,
legendData,
legendOrientation,
legendProps: Object.assign(Object.assign({}, legendProps), { itemsPerRow: i }),
padding,
theme,
width
});
if (fits) {
itemsPerRow = i;
break;
}
}
return itemsPerRow;
};
/**
* Returns the extra height required to accommodate wrapped legend items
*
* @private Not intended as public API and subject to change
*/
export const getLegendItemsExtraHeight = ({ legendData, legendOrientation, legendProps, theme }) => {
// Get legend dimensions
const legendDimensions = getLegendDimensions({
legendData,
legendOrientation,
legendProps,
theme
});
// Get legend dimensions without any wrapped items
const legendDimensionsNoWrap = getLegendDimensions({
legendData,
legendOrientation,
legendProps: Object.assign(Object.assign({}, legendProps), { itemsPerRow: undefined }),
theme
});
return Math.abs(legendDimensions.height - legendDimensionsNoWrap.height);
};
/**
* Returns x coordinate for legend
*
* @private Not intended as public API and subject to change
*/
const getLegendX = (_a) => {
var { chartType } = _a, rest = __rest(_a, ["chartType"]);
return chartType === 'pie' ? getPieLegendX(rest) : getChartLegendX(rest);
};
/**
* Returns y coordinate for legend
*
* @private Not intended as public API and subject to change
*/
const getLegendY = (_a) => {
var { chartType } = _a, rest = __rest(_a, ["chartType"]);
switch (chartType) {
case 'pie':
return getPieLegendY(rest);
case 'bullet':
return getBulletLegendY(rest);
default:
return getChartLegendY(rest);
}
};
/**
* Returns y coordinate for bullet legends
*
* @private Not intended as public API and subject to change
*/
const getBulletLegendY = ({ dy = 0, height, legendPosition, legendData, legendOrientation, legendProps, padding, theme, width }) => {
const { left, right } = Helpers.getPadding(padding);
const chartSize = {
height, // Fixed size
width: width - left - right
};
switch (legendPosition) {
case 'bottom':
case 'bottom-left':
return chartSize.height + ChartCommonStyles.legend.margin + dy;
case 'right': {
// Legend height with padding
const legendDimensions = getLegendDimensions({
legendData,
legendOrientation,
legendProps,
theme
});
const legendPadding = (legendDataArr) => (legendDataArr && legendDataArr.length > 0 ? 17 : 0);
return (chartSize.height - legendDimensions.height) / 2 + legendPadding(legendData);
}
default:
return dy;
}
};
/**
* Returns x coordinate for chart legends
*
* @private Not intended as public API and subject to change
*/
const getChartLegendX = ({ dx = 0, height, legendData, legendOrientation, legendPosition, legendProps, padding, theme, width }) => {
const { top, bottom, left, right } = Helpers.getPadding(padding);
const chartSize = {
height: Math.abs(height - (bottom + top)),
width: Math.abs(width - (left + right))
};
const legendDimensions = getLegendDimensions({
legendData,
legendOrientation,
legendProps,
theme
});
switch (legendPosition) {
case 'bottom':
return width > legendDimensions.width ? Math.round((width - legendDimensions.width) / 2) + dx : dx;
case 'bottom-left':
return left + dx;
case 'right':
return chartSize.width + ChartCommonStyles.legend.margin + left + dx;
default:
return dx;
}
};
/**
* Returns y coordinate for chart legends
*
* @private Not intended as public API and subject to change
*/
const getChartLegendY = ({ dy = 0, height, legendPosition, legendData, legendOrientation, legendProps, padding, theme, width }) => {
const { top, bottom, left, right } = Helpers.getPadding(padding);
const chartSize = {
height: Math.abs(height - (bottom + top)),
width: Math.abs(width - (left + right))
};
switch (legendPosition) {
case 'bottom':
case 'bottom-left':
return chartSize.height + ChartCommonStyles.legend.margin * 2 + top + dy;
case 'right': {
// Legend height with padding
const legendDimensions = getLegendDimensions({
legendData,
legendOrientation,
legendProps,
theme
});
const originX = chartSize.height / 2 + top;
const legendPadding = (legendDataArr) => (legendDataArr && legendDataArr.length > 0 ? 2 : 0);
return originX - legendDimensions.height / 2 + legendPadding(legendData);
}
default:
return dy;
}
};
/**
* Returns x coordinate for pie legends
*
* @private Not intended as public API and subject to change
*/
const getPieLegendX = ({ dx = 0, height, legendData, legendOrientation, legendPosition, legendProps, padding, theme, width }) => {
const origin = getPieOrigin({ height, padding, width });
const radius = Helpers.getRadius({ height, width, padding });
const legendDimensions = getLegendDimensions({
legendData,
legendOrientation,
legendProps,
theme
});
switch (legendPosition) {
case 'bottom':
return width > legendDimensions.width ? Math.round((width - legendDimensions.width) / 2) + dx : dx;
case 'right':
return origin.x + ChartCommonStyles.label.margin + dx + radius;
default:
return dx;
}
};
/**
* Returns y coordinate for pie legends
*
* @private Not intended as public API and subject to change
*/
const getPieLegendY = ({ dy = 0, height, legendPosition, legendData, legendOrientation, legendProps, padding, theme, width }) => {
const origin = getPieOrigin({ height, padding, width });
const radius = Helpers.getRadius({ height, width, padding });
switch (legendPosition) {
case 'bottom':
return origin.y + ChartCommonStyles.legend.margin + radius + dy;
case 'right': {
// Legend height with padding
const legendDimensions = getLegendDimensions({
legendData,
legendOrientation,
legendProps,
theme
});
const legendPadding = (legendDataArr) => (legendDataArr && legendDataArr.length > 0 ? 2 : 0);
return origin.y - legendDimensions.height / 2 + legendPadding(legendData);
}
default:
return dy;
}
};
/**
* Returns an approximation of longest text width based on legend styles
*
* @private Not intended as public API and subject to change
*/
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const getMaxLegendTextSize = ({ legendData, theme }) => {
const style = theme && theme.legend && theme.legend.style ? theme.legend.style.labels : undefined;
if (!(legendData && legendData.length)) {
return 0;
}
let result = '';
legendData.forEach((data) => {
if (data.name && data.name.length > result.length) {
result = data.name;
}
});
// The approximateTextSize function returns height and width, but Victory incorrectly typed it as number
const adjustedTextSize = TextSize.approximateTextSize(result, Object.assign({}, style));
return adjustedTextSize.width;
};
//# sourceMappingURL=chart-legend.js.map