UNPKG

dce-charts

Version:
595 lines (571 loc) 20.1 kB
'use strict'; Object.defineProperty(exports, '__esModule', { value: true }); var React = require('react'); var reactChartjs2 = require('react-chartjs-2'); var chart_js = require('chart.js'); var clone = require('fast-clone'); var Papa = require('papaparse'); var reactFontawesome = require('@fortawesome/react-fontawesome'); var freeSolidSvgIcons = require('@fortawesome/free-solid-svg-icons'); function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; } var React__default = /*#__PURE__*/_interopDefaultLegacy(React); var clone__default = /*#__PURE__*/_interopDefaultLegacy(clone); var Papa__default = /*#__PURE__*/_interopDefaultLegacy(Papa); /** * Get a css color based on a color object and opacity * @author Jackson Parsells * @param color the color object * @param opacity opacity of the color * @returns the css color as a string */ const getRGBA = (color, opacity) => { return `rgba(${color.r}, ${color.g}, ${color.b}, ${opacity})`; }; /** * Blue preset color * @author Gabe Abrams */ const BlueColor = { r: 54, g: 162, b: 235, }; /** * Green preset color * @author Gabe Abrams */ const GreenColor = { r: 75, g: 192, b: 192, }; /** * Orange preset color * @author Gabe Abrams */ const OrangeColor = { r: 255, g: 159, b: 64, }; /** * Purple preset color * @author Gabe Abrams */ const PurpleColor = { r: 153, g: 102, b: 255, }; /** * Red preset color * @author Gabe Abrams */ const RedColor = { r: 255, g: 99, b: 132, }; /** * Yellow preset color * @author Gabe Abrams */ const YellowColor = { r: 255, g: 206, b: 86, }; // Import colors /** * Preset available colors * @author Gabe Abrams */ const presetColors = { red: RedColor, blue: BlueColor, yellow: YellowColor, green: GreenColor, purple: PurpleColor, orange: OrangeColor, }; /** * Returns true if the two colors are the same * @author Gabe Abrams * @param a first color * @param b second color * @returns true if colors are the same */ const colorsAreSame = (a, b) => { return (a.r === b.r && a.g === b.g && a.b === b.b); }; // Import libs // List of all colors in the order they will be distributed const allColors = [ presetColors.red, presetColors.blue, presetColors.yellow, presetColors.green, presetColors.orange, presetColors.purple, ]; /** * Add colors to elements that don't have colors * @author Gabe Abrams * @param items list of items that may or may not have a color prop * @returns items with a color prop defined on every item */ const addColors = (items) => { var _a; // If one or zero items, this is simple: just give it a color if (items.length === 0) { return []; } if (items.length === 1) { return [Object.assign(Object.assign({}, items), { color: ((_a = items[0].color) !== null && _a !== void 0 ? _a : allColors[0]) })]; } // first clone items const itemsClone = clone__default["default"](items); // Index of the next color let nextColorIndex = 0; itemsClone.forEach((item, index) => { // If user includes a color, just keep it if (item.color) { return; } // Get the index of the previous and next colors const prevItemColor = itemsClone[(index === 0 ? itemsClone.length - 1 : index - 1)]; const nextItemColor = itemsClone[(index === itemsClone.length - 1 ? 0 : index + 1)]; // Loop until we get a unique color let color = allColors[nextColorIndex]; for (let i = 0; i < 3; i++) { // Get the next candidate color const candidateColor = allColors[(nextColorIndex + i) % allColors.length]; // Check if this candidate color matches the prev/next color const candidateColorIsSame = (colorsAreSame(candidateColor, prevItemColor) || colorsAreSame(candidateColor, nextItemColor)); // If this is a unique color, keep it! if (!candidateColorIsSame) { // Save the color color = candidateColor; // Increment next color index nextColorIndex += (i + 1); break; } } // Save the color to the item itemsClone[index].color = color; }); // return the cloned list return itemsClone; }; /** * Component that's a clickable button to download the contents of a chart as * a CSV file * @author Jackson Parsells * @author Gabe Abrams */ /*------------------------------------------------------------------------*/ /* Component */ /*------------------------------------------------------------------------*/ const DownloadButton = (props) => { /*------------------------------------------------------------------------*/ /* Setup */ /*------------------------------------------------------------------------*/ /* -------------- Props ------------- */ // destructure props const { title, chartData, filename, } = props; /*------------------------------------------------------------------------*/ /* Render */ /*------------------------------------------------------------------------*/ /*----------------------------------------*/ /* Main UI */ /*----------------------------------------*/ // Generate CSV text const csvText = Papa__default["default"].unparse(chartData); // Download button return (React__default["default"].createElement("a", { href: `data:text/csv;charset=utf-8,${encodeURI(csvText)}`, download: filename, className: "btn btn-sm btn-primary", style: { borderTopLeftRadius: 0, borderBottomRightRadius: 0, }, title: "Download Data as CSV", "aria-label": `download the data of ${title} as a csv file` }, React__default["default"].createElement(reactFontawesome.FontAwesomeIcon, { icon: freeSolidSvgIcons.faDownload }))); }; /** * Download box container that wraps a chart * @author Gabe Abrams * @author Jackson Parsells */ /*------------------------------------------------------------------------*/ /* Style */ /*------------------------------------------------------------------------*/ const style = ` .DownloadBox-download { position: absolute; top: 0; right: 15px; cursor: pointer; } .DownloadBox-no-outline { border: 1px solid transparent; } .DownloadBox-outlined { border: 1px solid #007bff; } `; /* ------------- Actions ------------ */ // Types of actions var ActionType; (function (ActionType) { // Start hovering ActionType["StartHovering"] = "start-hovering"; // Stop hovering ActionType["StopHovering"] = "stop-hovering"; })(ActionType || (ActionType = {})); /** * Reducer that executes actions * @author Gabe Abrams * @param state current state * @param action action to execute */ const reducer = (state, action) => { switch (action.type) { case ActionType.StartHovering: { return Object.assign(Object.assign({}, state), { hovering: true }); } case ActionType.StopHovering: { return Object.assign(Object.assign({}, state), { hovering: false }); } default: { return state; } } }; /*------------------------------------------------------------------------*/ /* Component */ /*------------------------------------------------------------------------*/ const DownloadBox = (props) => { /*------------------------------------------------------------------------*/ /* Setup */ /*------------------------------------------------------------------------*/ /* -------------- Props ------------- */ // Destructure all props const { title, chartData, children, } = props; /* -------------- State ------------- */ // Initial state const initialState = { hovering: false, }; // Initialize state const [state, dispatch] = React.useReducer(reducer, initialState); // Destructure common state const { hovering, } = state; /*------------------------------------------------------------------------*/ /* Render */ /*------------------------------------------------------------------------*/ /*----------------------------------------*/ /* Main UI */ /*----------------------------------------*/ return (React__default["default"].createElement("div", { // use similar logic to below to add for focus and hovering className: hovering ? 'DownloadBox-outlined' : 'DownloadBox-no-outline', onMouseEnter: () => { dispatch({ type: ActionType.StartHovering }); }, onFocus: () => { dispatch({ type: ActionType.StartHovering }); }, onMouseLeave: () => { dispatch({ type: ActionType.StopHovering }); }, onBlur: () => { dispatch({ type: ActionType.StopHovering }); } }, React__default["default"].createElement("style", null, style), children, React__default["default"].createElement("div", { className: `DownloadBox-download ${hovering ? '' : 'sr-only'}` }, React__default["default"].createElement(DownloadButton, { title: title, chartData: chartData, filename: `${title}.csv` })))); }; /** * DoughnutChart, wrapper around the ChartJS Doughnut chart for DCE widgets * @author Jackson Parsells */ // chart JS registry chart_js.Chart.register(chart_js.ArcElement, chart_js.Tooltip, chart_js.Legend); /*------------------------------------------------------------------------*/ /* Component */ /*------------------------------------------------------------------------*/ const DoughnutChart = (props) => { /*------------------------------------------------------------------------*/ /* Setup */ /*------------------------------------------------------------------------*/ /* -------------- Props ------------- */ const { title, showTitle = true, segments, showLegend = true, } = props; // Add colors to props const segmentsWithColors = addColors(segments); // Create label list const labels = segmentsWithColors.map((segment) => { return segment.label; }); // Create dataset const dataset = { label: '', data: segmentsWithColors.map((segment) => { return segment.value; }), backgroundColor: segmentsWithColors.map((segment) => { return getRGBA(segment.color, 0.2); }), borderColor: segmentsWithColors.map((segment) => { return getRGBA(segment.color, 1); }), borderWidth: 1, }; // data for CSV const dataForCSV = segments.map((segment) => { return { Name: segment.label, Value: segment.value, }; }); // chart data const chartData = { labels, datasets: [dataset], }; // chart options const options = { responsive: true, plugins: { legend: { display: showLegend, position: 'top', }, title: { display: showTitle, text: title, }, }, }; /*----------------------------------------*/ /* Main UI */ /*----------------------------------------*/ return (React__default["default"].createElement(DownloadBox, { chartData: dataForCSV, title: title }, React__default["default"].createElement(reactChartjs2.Doughnut, { data: chartData, options: options }))); }; /** * LineChart, wrapper around the ChartJS Line chart for DCE widgets * @author Jackson Parsells */ // chart JS registry chart_js.Chart.register(chart_js.CategoryScale, chart_js.LinearScale, chart_js.PointElement, chart_js.LineElement, chart_js.Title, chart_js.Tooltip, chart_js.Legend); /*------------------------------------------------------------------------*/ /* Component */ /*------------------------------------------------------------------------*/ const LineChart = (props) => { /*------------------------------------------------------------------------*/ /* Setup */ /*------------------------------------------------------------------------*/ /* -------------- Props ------------- */ const { title, showTitle = true, points, } = props; // Add colors to props const pointsWithColors = addColors(points); // Create label list const labels = pointsWithColors.map((point) => { return point.label; }); // Create dataset const dataset = { label: '', data: pointsWithColors.map((point) => { return point.value; }), backgroundColor: pointsWithColors.map((point) => { return getRGBA(point.color, 0.2); }), borderColor: pointsWithColors.map((point) => { return getRGBA(point.color, 1); }), borderWidth: 1, }; // data for CSV const dataForCSV = points.map((point) => { return { Name: point.label, Value: point.value, }; }); // chart data const chartData = { labels, datasets: [dataset], }; // chart options const options = { responsive: true, plugins: { legend: { display: false, position: 'top', }, title: { text: title, display: showTitle, }, }, }; /*----------------------------------------*/ /* Main UI */ /*----------------------------------------*/ return (React__default["default"].createElement(DownloadBox, { chartData: dataForCSV, title: title }, React__default["default"].createElement(reactChartjs2.Line, { data: chartData, options: options }))); }; /** * HorizontalBarChart, wrapper around the ChartJS Horizontal Bar chart for DCE * widgets * @author Jackson Parsells */ // chart JS registry chart_js.Chart.register(chart_js.CategoryScale, chart_js.LinearScale, chart_js.BarElement, chart_js.Title, chart_js.Tooltip, chart_js.Legend); /*------------------------------------------------------------------------*/ /* Component */ /*------------------------------------------------------------------------*/ const HorizontalBarChart = (props) => { /*------------------------------------------------------------------------*/ /* Setup */ /*------------------------------------------------------------------------*/ /* -------------- Props ------------- */ // destructure props const { title, showTitle = true, bars, } = props; // add colors to props const barsWithColors = addColors(bars); // create label list const labels = barsWithColors.map((bar) => { return bar.label; }); // create dataset const dataset = { label: '', data: barsWithColors.map((bar) => { return bar.value; }), backgroundColor: barsWithColors.map((bar) => { return getRGBA(bar.color, 0.2); }), borderColor: barsWithColors.map((bar) => { return getRGBA(bar.color, 1); }), borderWidth: 1, }; // data for CSV const dataForCSV = bars.map((bar) => { return { Name: bar.label, Value: bar.value, }; }); // chart data const chartData = { labels, datasets: [dataset], }; // chart options to have the bars extend horizontally const options = { indexAxis: 'y', responsive: true, plugins: { legend: { display: false, position: 'top', }, title: { display: showTitle, text: title, }, }, }; /*----------------------------------------*/ /* Main UI */ /*----------------------------------------*/ return (React__default["default"].createElement(DownloadBox, { chartData: dataForCSV, title: title }, React__default["default"].createElement(reactChartjs2.Bar, { data: chartData, options: options }))); }; /** * VerticalBarChart, wrapper around the ChartJS Vertical Bar chart for DCE * widgets * @author Jackson Parsells */ // chart JS registry chart_js.Chart.register(chart_js.CategoryScale, chart_js.LinearScale, chart_js.BarElement, chart_js.Title, chart_js.Tooltip, chart_js.Legend); /*------------------------------------------------------------------------*/ /* Component */ /*------------------------------------------------------------------------*/ const VerticalBarChart = (props) => { /*------------------------------------------------------------------------*/ /* Setup */ /*------------------------------------------------------------------------*/ /* -------------- Props ------------- */ const { title, showTitle = true, bars, } = props; // add colors to props const barsWithColors = addColors(bars); // create label list const labels = barsWithColors.map((bar) => { return bar.label; }); // create dataset const dataset = { label: '', data: barsWithColors.map((bar) => { return bar.value; }), backgroundColor: barsWithColors.map((bar) => { return getRGBA(bar.color, 0.2); }), borderColor: barsWithColors.map((bar) => { return getRGBA(bar.color, 1); }), borderWidth: 1, }; // data for CSV const dataForCSV = bars.map((bar) => { return { Name: bar.label, Value: bar.value, }; }); // chart data const chartData = { labels, datasets: [dataset], }; // chart options to make the bar vertical const options = { responsive: true, plugins: { legend: { display: false, position: 'top', }, title: { display: showTitle, text: title, }, }, }; /*----------------------------------------*/ /* Main UI */ /*----------------------------------------*/ return (React__default["default"].createElement(DownloadBox, { chartData: dataForCSV, title: title }, React__default["default"].createElement(reactChartjs2.Bar, { data: chartData, options: options }))); }; exports.BlueColor = BlueColor; exports.DoughnutChart = DoughnutChart; exports.GreenColor = GreenColor; exports.HorizontalBarChart = HorizontalBarChart; exports.LineChart = LineChart; exports.OrangeColor = OrangeColor; exports.PurpleColor = PurpleColor; exports.RedColor = RedColor; exports.VerticalBarChart = VerticalBarChart; exports.YellowColor = YellowColor; //# sourceMappingURL=index.js.map