UNPKG

lucid-ui

Version:

A UI component library from Xandr.

361 lines (353 loc) 17.9 kB
"use strict"; var __assign = (this && this.__assign) || function () { __assign = Object.assign || function(t) { for (var s, i = 1, n = arguments.length; i < n; i++) { s = arguments[i]; for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p]; } return t; }; return __assign.apply(this, arguments); }; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); __setModuleDefault(result, mod); return result; }; var __rest = (this && this.__rest) || function (s, e) { var t = {}; for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0) t[p] = s[p]; if (s != null && typeof Object.getOwnPropertySymbols === "function") for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) { if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i])) t[p[i]] = s[p[i]]; } return t; }; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.BarChart = void 0; var lodash_1 = __importDefault(require("lodash")); var react_1 = __importDefault(require("react")); var prop_types_1 = __importDefault(require("prop-types")); var d3Scale = __importStar(require("d3-scale")); var chartConstants = __importStar(require("../../constants/charts")); var style_helpers_1 = require("../../util/style-helpers"); var component_types_1 = require("../../util/component-types"); var chart_helpers_1 = require("../../util/chart-helpers"); var Axis_1 = __importDefault(require("../Axis/Axis")); var AxisLabel_1 = __importDefault(require("../AxisLabel/AxisLabel")); var Bars_1 = __importDefault(require("../Bars/Bars")); var ContextMenu_1 = __importDefault(require("../ContextMenu/ContextMenu")); var Legend_1 = __importDefault(require("../Legend/Legend")); var EmptyStateWrapper_1 = __importDefault(require("../EmptyStateWrapper/EmptyStateWrapper")); var cx = style_helpers_1.lucidClassNames.bind('&-BarChart'); var arrayOf = prop_types_1.default.arrayOf, func = prop_types_1.default.func, number = prop_types_1.default.number, object = prop_types_1.default.object, shape = prop_types_1.default.shape, string = prop_types_1.default.string, array = prop_types_1.default.array, bool = prop_types_1.default.bool, oneOfType = prop_types_1.default.oneOfType, oneOf = prop_types_1.default.oneOf; var defaultProps = { height: 400, width: 1000, // duplicated because `statics` aren't available during getDefaultProps margin: { top: 10, right: 20, bottom: 50, left: 80, }, palette: chartConstants.PALETTE_7, hasToolTips: true, hasLegend: false, renderTooltipBody: null, xAxisField: 'x', xAxisTickCount: null, xAxisTitle: null, xAxisTitleColor: '#000', xAxisFormatter: lodash_1.default.identity, xAxisTextOrientation: 'horizontal', yAxisFields: ['y'], yAxisTickCount: null, yAxisIsStacked: false, yAxisMin: 0, yAxisTitle: null, yAxisTitleColor: '#000', yAxisTooltipFormatter: function (yField, yValueFormatted) { return "".concat(yField, ": ").concat(yValueFormatted); }, yAxisTextOrientation: 'horizontal', }; var BarChart = function (props) { var className = props.className, height = props.height, width = props.width, marginOriginal = props.margin, data = props.data, legend = props.legend, isLoading = props.isLoading, hasToolTips = props.hasToolTips, hasLegend = props.hasLegend, palette = props.palette, colorMap = props.colorMap, renderTooltipBody = props.renderTooltipBody, xAxisField = props.xAxisField, xAxisFormatter = props.xAxisFormatter, xAxisTitle = props.xAxisTitle, xAxisTitleColor = props.xAxisTitleColor, xAxisTickCount = props.xAxisTickCount, xAxisTextOrientation = props.xAxisTextOrientation, yAxisFields = props.yAxisFields, yAxisFormatter = props.yAxisFormatter, yAxisTitle = props.yAxisTitle, yAxisTitleColor = props.yAxisTitleColor, yAxisIsStacked = props.yAxisIsStacked, yAxisTickCount = props.yAxisTickCount, yAxisMin = props.yAxisMin, yAxisTooltipFormatter = props.yAxisTooltipFormatter, yAxisTooltipDataFormatter = props.yAxisTooltipDataFormatter, _a = props.yAxisMax, yAxisMax = _a === void 0 ? yAxisIsStacked ? (0, chart_helpers_1.maxByFieldsStacked)(data, yAxisFields) : (0, chart_helpers_1.maxByFields)(data, yAxisFields) : _a, yAxisTextOrientation = props.yAxisTextOrientation, passThroughs = __rest(props, ["className", "height", "width", "margin", "data", "legend", "isLoading", "hasToolTips", "hasLegend", "palette", "colorMap", "renderTooltipBody", "xAxisField", "xAxisFormatter", "xAxisTitle", "xAxisTitleColor", "xAxisTickCount", "xAxisTextOrientation", "yAxisFields", "yAxisFormatter", "yAxisTitle", "yAxisTitleColor", "yAxisIsStacked", "yAxisTickCount", "yAxisMin", "yAxisTooltipFormatter", "yAxisTooltipDataFormatter", "yAxisMax", "yAxisTextOrientation"]); var margin = __assign(__assign({}, exports.BarChart.MARGIN), marginOriginal); var svgClasses = cx(className, '&'); var innerWidth = width - margin.left - margin.right; var innerHeight = height - margin.top - margin.bottom; // `paddingInner` determines the space between the bars or groups of bars var paddingInner = yAxisFields.length > 1 ? exports.BarChart.PADDING_GROUPED_OR_STACKED : exports.BarChart.PADDING; var xScale = d3Scale .scaleBand() .domain(lodash_1.default.map(data, xAxisField)) .range([0, innerWidth]) .paddingInner(paddingInner) .paddingOuter(0.5); var yScale = d3Scale .scaleLinear() .domain([yAxisMin, yAxisMax]) .range([innerHeight, 0]); // @ts-ignore var xAxisFinalFormatter = xAxisFormatter || xScale.tickFormat(); var yAxisFinalFormatter = yAxisFormatter || yScale.tickFormat(); var yFinalFormatter = yAxisTooltipDataFormatter ? yAxisTooltipDataFormatter : yAxisFinalFormatter; if (lodash_1.default.isEmpty(data) || width < 1 || height < 1 || isLoading) { var emptyStateWrapper = (0, component_types_1.getFirst)(props, exports.BarChart.EmptyStateWrapper) || react_1.default.createElement(exports.BarChart.EmptyStateWrapper, { Title: 'You have no data.' }); return (react_1.default.createElement(EmptyStateWrapper_1.default, __assign({}, emptyStateWrapper.props, { isEmpty: lodash_1.default.isEmpty(data), isLoading: isLoading }), emptyStateWrapper.props.children, react_1.default.createElement("svg", __assign({}, passThroughs, { className: svgClasses, width: width, height: height }), react_1.default.createElement("g", { transform: "translate(".concat(margin.left, ", ").concat(innerHeight + margin.top, ")") }, react_1.default.createElement(Axis_1.default, { orient: 'bottom', scale: xScale, tickCount: xAxisTickCount })), react_1.default.createElement("g", { transform: "translate(".concat(margin.left, ", ").concat(margin.top, ")") }, react_1.default.createElement(Axis_1.default, { orient: 'left', scale: yScale, tickFormat: yFinalFormatter, tickCount: yAxisTickCount }))))); } return (react_1.default.createElement("svg", __assign({}, passThroughs, { className: svgClasses, width: width, height: height }), react_1.default.createElement("g", { transform: "translate(".concat(margin.left, ", ").concat(innerHeight + margin.top, ")") }, react_1.default.createElement(Axis_1.default, { orient: 'bottom', // @ts-ignore scale: xScale, outerTickSize: 0, tickFormat: xAxisFinalFormatter, tickCount: xAxisTickCount, textOrientation: xAxisTextOrientation }), hasLegend ? (react_1.default.createElement(ContextMenu_1.default, { direction: 'down', alignment: 'center', directonOffset: (margin.bottom / 2 + Legend_1.default.HEIGHT / 2) * -1 /* should center the legend in the bottom margin */ }, react_1.default.createElement(ContextMenu_1.default.Target, { elementType: 'g' }, react_1.default.createElement("rect", { className: cx('&-invisible'), width: innerWidth, height: margin.bottom })), react_1.default.createElement(ContextMenu_1.default.FlyOut, { className: cx('&-legend-container') }, react_1.default.createElement(Legend_1.default, { orient: 'horizontal' }, lodash_1.default.map(yAxisFields, function (field, index) { return (react_1.default.createElement(Legend_1.default.Item, { key: index, hasPoint: true, hasLine: false, color: lodash_1.default.get(colorMap, field, palette[index % palette.length]), pointKind: 1 }, lodash_1.default.get(legend, field, field))); }))))) : null), xAxisTitle ? (react_1.default.createElement("g", { transform: "translate(".concat(margin.left, ", ").concat(margin.top + innerHeight, ")") }, react_1.default.createElement(AxisLabel_1.default, { orient: 'bottom', width: innerWidth, height: margin.bottom, label: xAxisTitle, color: lodash_1.default.isString(xAxisTitleColor) ? xAxisTitleColor : palette[xAxisTitleColor % palette.length] }))) : null, react_1.default.createElement("g", { transform: "translate(".concat(margin.left, ", ").concat(margin.top, ")") }, react_1.default.createElement(Axis_1.default, { orient: 'left', scale: yScale, tickFormat: yAxisFinalFormatter, tickCount: yAxisTickCount, textOrientation: yAxisTextOrientation })), yAxisTitle ? (react_1.default.createElement("g", { transform: "translate(0, ".concat(margin.top, ")") }, react_1.default.createElement(AxisLabel_1.default, { orient: 'left', width: margin.left, height: innerHeight, label: yAxisTitle, color: lodash_1.default.isString(yAxisTitleColor) ? yAxisTitleColor : palette[yAxisTitleColor % palette.length] }))) : null, react_1.default.createElement("g", { transform: "translate(".concat(margin.left, ", ").concat(margin.top, ")") }, react_1.default.createElement(Bars_1.default, { xField: xAxisField, xScale: xScale, xFormatter: xAxisFormatter, yFields: yAxisFields, yScale: yScale, // @ts-ignore yFormatter: yFinalFormatter, yStackedMax: yAxisMax, data: data, isStacked: yAxisIsStacked, yTooltipFormatter: yAxisTooltipFormatter, hasToolTips: hasToolTips, legend: legend, palette: palette, colorMap: colorMap, renderTooltipBody: renderTooltipBody })))); }; exports.BarChart = BarChart; exports.BarChart.displayName = 'BarChart'; exports.BarChart.propTypes = { /** Appended to the component-specific class names set on the root element. */ className: string, /** Height of the chart. */ height: number, /** Width of the chart. */ width: number, /** An object defining the margins of the chart. These margins typically contain the axis and labels. */ margin: shape({ top: number, right: number, bottom: number, left: number, }), /** Data for the chart. E.g. [ { x: 'Monday' , y: 1 } , { x: 'Tuesday' , y: 2 } , { x: 'Wednesday' , y: 3 } , { x: 'Thursday' , y: 2 } , { x: 'Friday' , y: 5 } , ] */ data: arrayOf(object), /** An object with human readable names for fields that will be used for legends and tooltips. E.g: { x: 'Date', y: 'Impressions', } */ legend: object, /** Controls the visibility of the \`LoadingMessage\`. */ isLoading: bool, /** Show tool tips on hover. */ hasToolTips: bool, /** Show a legend at the bottom of the chart. */ hasLegend: bool, /** Takes one of the palettes exported from \`lucid.chartConstants\`. Available palettes: - \`PALETTE_7\` (default) - \`PALETTE_30\` - \`PALETTE_MONOCHROME_0_5\` - \`PALETTE_MONOCHROME_1_5\` - \`PALETTE_MONOCHROME_2_5\` - \`PALETTE_MONOCHROME_3_5\` - \`PALETTE_MONOCHROME_4_5\` - \`PALETTE_MONOCHROME_5_5\` - \`PALETTE_MONOCHROME_6_5\` */ palette: arrayOf(string), /** You can pass in an object if you want to map x values to \`lucid.chartConstants\` or custom colors: { 'imps': COLOR_0, 'rev': COLOR_3, 'clicks': '#abc123', } */ colorMap: object, /** The field we should look up your x data by. Your actual x data must be strings. */ xAxisField: string, /** There are some cases where you need to only show a "sampling" of ticks on the x axis. This number will control that. */ xAxisTickCount: number, /** An optional function used to format your x axis data. If you don't provide anything, we'll use an identity function. */ xAxisFormatter: func, /** Set a title for the x axis. */ xAxisTitle: string, /** Set a color for the x axis title. Use the color constants exported off \`lucid.chartConstants\`. E.g.: - \`COLOR_0\` - \`COLOR_GOOD\` - \`'#123abc'\` // custom color hex \`number\` is supported only for backwards compatability. */ xAxisTitleColor: oneOfType([number, string]), /** An array of your y axis fields. Typically this will just be a single item unless you need to display grouped or stacked bars. The order of the array determines the series order in the chart. */ yAxisFields: array, /** The minimum number the y axis should display. Typically this should be be \`0\`. */ yAxisMin: number, /** The maximum number the y axis should display. This should almost always be the largest number from your dataset. */ yAxisMax: number, /** An optional function used to format your y axis data. If you don't provide anything, we use the default D3 number formatter. */ yAxisFormatter: func, /** Stack the y axis data instead of showing it as groups. This is only useful if you have multiple \`yAxisFields\`. Stacking will cause the chart to be aggregated by sum. */ yAxisIsStacked: bool, /** There are some cases where you need to only show a "sampling" of ticks on the y axis. This number will control that. */ yAxisTickCount: number, /** Set a title for the y axis. */ yAxisTitle: string, /** Set a color for the y axis title. Use the color constants exported off \`lucid.chartConstants\`. E.g.: - \`COLOR_0\` - \`COLOR_GOOD\` - \`'#123abc'\` // custom color hex \`number\` is supported only for backwards compatability. */ yAxisTitleColor: oneOfType([number, string]), /** An optional function used to format your y axis titles and data in the tooltip legends. The first value is the name of your y field, the second value is your post-formatted y value, and the third value is your non-formatted y-value. Signature: \`(yField, yValueFormatted, yValue) => {}\` */ yAxisTooltipFormatter: func, /** An optional function used to format y-values in the tooltip legends. */ yAxisTooltipDataFormatter: func, /** An optional function used to format the entire tooltip body. The only arg is the associated data point. This formatter will over-ride yAxisTooltipFormatter and yAxisTooltipDataFormatter. Signature: \`dataPoint => {}\` */ renderTooltipBody: func, /** Determines the orientation of the tick text. This may override what the orient prop tries to determine. */ xAxisTextOrientation: oneOf(['vertical', 'horizontal', 'diagonal']), /** Determines the orientation of the tick text. This may override what the orient prop tries to determine. */ yAxisTextOrientation: oneOf(['vertical', 'horizontal', 'diagonal']), }; exports.BarChart.defaultProps = defaultProps; exports.BarChart.peek = { description: "A `Bar Chart` is great for showing data that fits neatly into \"buckets\". The x axis data must be strings, and the y axis data must be numeric.", categories: ['visualizations', 'charts'], madeFrom: ['ContextMenu', 'ToolTip'], }; exports.BarChart.EmptyStateWrapper = EmptyStateWrapper_1.default; exports.BarChart.PADDING = 0.05; exports.BarChart.PADDING_GROUPED_OR_STACKED = 0.3; exports.BarChart.MARGIN = { top: 10, right: 20, bottom: 50, left: 80, }; exports.default = exports.BarChart; //# sourceMappingURL=BarChart.js.map