UNPKG

@springernature/nn-charts

Version:
830 lines (765 loc) 36.7 kB
"use strict"; function _typeof(o) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) { return typeof o; } : function (o) { return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o; }, _typeof(o); } Object.defineProperty(exports, "__esModule", { value: true }); exports.updateNestedProperties = exports.rgbToHex = exports.resetChart = exports.positionAxis = exports.numberWithCommas = exports.mouseover = exports.mouseleave = exports.modifyAxisLabels = exports.makeXAxisLabelAdaptations = exports.getValueUpperLimit = exports.getStackMax = exports.getScaleTimeAxisScope = exports.getScaleBandAxisScope = exports.getGroupMax = exports.getElementBoundingClientRect = exports.getDataToDrawColumnChartCategories = exports.getContainerWidth = exports.getContainerHeight = exports.getColumnChartAxisData = exports.getChartMargins = exports.generateLayersForColumnChart = exports.generateLayerGroupsForColumnChart = exports.generateBarsForColumnChart = exports.createScaleBandXAxis = exports.createLinearYAxis = exports.createAndAppendPath = exports.columnChartOnWindowResize = exports.clickChart = exports.clearSvgBasePanel = exports.changeChart = exports.applyTheme = exports.applyStrokeStyle = exports.appendYAxisTitle = exports.appendYAxisGridLines = exports.appendXAxisTitle = exports.appendElementToTarget = exports.appendAxis = exports.alertSize = void 0; var d3 = _interopRequireWildcard(require("d3")); var _columnTransition = require("./column-transition"); var _tooltips = require("./tooltips"); var _this = void 0; function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r = new WeakMap(), n = new WeakMap(); return (_interopRequireWildcard = function _interopRequireWildcard(e, t) { if (!t && e && e.__esModule) return e; var o, i, f = { __proto__: null, "default": e }; if (null === e || "object" != _typeof(e) && "function" != typeof e) return f; if (o = t ? n : r) { if (o.has(e)) return o.get(e); o.set(e, f); } for (var _t in e) "default" !== _t && {}.hasOwnProperty.call(e, _t) && ((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, _t)) && (i.get || i.set) ? o(f, _t, i) : f[_t] = e[_t]); return f; })(e, t); } function _slicedToArray(r, e) { return _arrayWithHoles(r) || _iterableToArrayLimit(r, e) || _unsupportedIterableToArray(r, e) || _nonIterableRest(); } function _nonIterableRest() { throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } function _unsupportedIterableToArray(r, a) { if (r) { if ("string" == typeof r) return _arrayLikeToArray(r, a); var t = {}.toString.call(r).slice(8, -1); return "Object" === t && r.constructor && (t = r.constructor.name), "Map" === t || "Set" === t ? Array.from(r) : "Arguments" === t || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(t) ? _arrayLikeToArray(r, a) : void 0; } } function _arrayLikeToArray(r, a) { (null == a || a > r.length) && (a = r.length); for (var e = 0, n = Array(a); e < a; e++) n[e] = r[e]; return n; } function _iterableToArrayLimit(r, l) { var t = null == r ? null : "undefined" != typeof Symbol && r[Symbol.iterator] || r["@@iterator"]; if (null != t) { var e, n, i, u, a = [], f = !0, o = !1; try { if (i = (t = t.call(r)).next, 0 === l) { if (Object(t) !== t) return; f = !1; } else for (; !(f = (e = i.call(t)).done) && (a.push(e.value), a.length !== l); f = !0); } catch (r) { o = !0, n = r; } finally { try { if (!f && null != t["return"] && (u = t["return"](), Object(u) !== u)) return; } finally { if (o) throw n; } } return a; } } function _arrayWithHoles(r) { if (Array.isArray(r)) return r; } function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; } function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; } function _defineProperty(e, r, t) { return (r = _toPropertyKey(r)) in e ? Object.defineProperty(e, r, { value: t, enumerable: !0, configurable: !0, writable: !0 }) : e[r] = t, e; } function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == _typeof(i) ? i : i + ""; } function _toPrimitive(t, r) { if ("object" != _typeof(t) || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != _typeof(i)) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); } /* METHODS LISTED BELOW THIS IN THIS SECTION ARE MORE GENERAL IN PURPOSE AND RELATE TO PRODUCING ANY CHART TYPE */ /* name: createScaleBandXAxis desc: create and define a new scaledBand type x-axis scale */ var createScaleBandXAxis = exports.createScaleBandXAxis = function createScaleBandXAxis(xAxisData, dataRange) { // // define chart x axis definition // range - max width of x axis var scaleBand_xAxis = d3.scaleBand().domain(d3.range(xAxisData.length)).rangeRound([0, dataRange]).padding(0.4).align(0.1); // // initilise count iterator var counter = 0; // // modify tick class name declaration and visible text content d3.selectAll(".x-axis").selectAll(".tick").attr("class", function () { // // add additional classes to x-axis tick labels... // and update tick label text d3.select(this).selectAll("text").attr("class", function () { return "tick__label xAxisTickLabels tick__label-" + counter; }).style("display", "inline"); // // add additional classes to x-axis tick lines... d3.select(this).selectAll("line").attr("class", function () { return "tick__tick-line tick__tick-line-" + counter; }); return "tick tick-" + counter++; // class name for final tick group }); return scaleBand_xAxis; }; // end method createScaleBandXAxis /* name: modifyAxisLabels desc: modify axis labels to more user-friendly meaningful values */ var modifyAxisLabels = exports.modifyAxisLabels = function modifyAxisLabels(axisOrientation, data, xAxisDataKey) { // // initilise count iterator var tickCounter = -1; d3.selectAll("." + axisOrientation + "-axis.axis").selectAll(".tick").selectAll("text").text(function () { tickCounter++; return data[tickCounter][xAxisDataKey]; }); }; // end method modifyAxisLabels /* name: getColumnChartAxisData desc: get data to define axis with ... */ var getColumnChartAxisData = exports.getColumnChartAxisData = function getColumnChartAxisData(data, key) { return data.map(function (item) { return item[key]; }); }; /* name: createLinearYAxis desc: create definition for a linear scale y-axis */ var createLinearYAxis = exports.createLinearYAxis = function createLinearYAxis(height, yAxisMax, roundingMax) { // var linear_yAxis = d3.scaleLinear().domain([0, Math.ceil(yAxisMax / getValueUpperLimit(yAxisMax)) * getValueUpperLimit(yAxisMax)]).rangeRound([height, 0]); return linear_yAxis; }; // end method createLinearYAxis /* name: appendYAxisGridLines desc: append full width grid lines to y-axis */ var appendYAxisGridLines = exports.appendYAxisGridLines = function appendYAxisGridLines(width, gridLines, classNames) { if (gridLines.enable == true) { // // remove all y-axis tick gridlines before redraw d3.selectAll(".tick__grid-line").remove(); // // draw tick grid lines extending from y-axis ticks on axis across full width of graph var yticks = d3.selectAll("." + classNames.join(".")).selectAll(".tick"); // yticks.append("svg:line").attr("class", "tick__grid-line").attr("y0", 0).attr("y1", 0).attr("x1", 0).attr("x2", width); } // end if ... return; }; // end method appendYAxisGridLines /* TODO - good candidate to write a generic function for appending axis title name: appendYAxisTitle desc: append main title to y-axis */ var appendYAxisTitle = exports.appendYAxisTitle = function appendYAxisTitle(title, height, axis) { // if (title.enable == true) { // initialise and append y-axis main title label d3.selectAll("." + axis.classNames.join(".")).append("text").attr("class", "title title__y-axis").attr("id", "yAxisTitleLeft").attr("x", function () { return title.orientation == "vertical" ? -height / 2 : 0; }).attr("y", function () { return title.axisOffset; }).attr("dy", "0em").style("text-anchor", title.textAnchor).style("transform", "rotate(" + title.rotation + ")").text(title.text); } return; }; // end method appendYAxisTitle /* TODO - good candidate to write a generic function for appending axis title name: appendXAxisTitle desc: append main title to x-axis */ var appendXAxisTitle = exports.appendXAxisTitle = function appendXAxisTitle(title, width, axis) { // if (title.enable == true) { // // initialise and append y-axis main title label d3.selectAll("." + axis.classNames.join(".")).append("text").attr("class", "title title__x-axis").attr("id", "xAxisTitle").attr("x", function () { return width / 2; }).attr("y", function () { return title.axisOffset; }).attr("dy", "0em").style("text-anchor", title.textAnchor).style("transform", "rotate(" + title.rotation + ")").text(title.text); } return; }; /* name: positionAxis desc: define position of an x- or y-axis on a chart */ var positionAxis = exports.positionAxis = function positionAxis(axisDefinition, axis) { var axisFunctions = { bottom: d3.axisBottom, top: d3.axisTop, left: d3.axisLeft, right: d3.axisRight }; var selectedAxisFunction = axisFunctions[axis.position]; return selectedAxisFunction ? selectedAxisFunction().scale(axisDefinition) : null; }; var getValueUpperLimit = exports.getValueUpperLimit = function getValueUpperLimit(value) { if (value) { return [1, 10, 100, 1000, 10000, 100000, 1000000, 1000000].filter(function (param) { return JSON.stringify(param).length == JSON.stringify(value).length; })[0]; } else 1; }; /* name: appendAxis desc: physically append an x- or y-axis to a chart */ var appendAxis = exports.appendAxis = function appendAxis(axisPosition, axis, svgPanel, chartHeight) { // var appended_Axis = svgPanel.append("g").attr("class", axis.classNames.join(" ")).attr("transform", "translate(0, ".concat(chartHeight, ")")).call(axisPosition); // return appended_Axis; }; // end method appendAxis /* name: getElementBoundingClientRect desc: determine boundingclientRect for selected DOM element. */ var getElementBoundingClientRect = exports.getElementBoundingClientRect = function getElementBoundingClientRect(element) { // store client boudning rectangle for selected element if (element) { var _d3$select; var boundingClientRect = (_d3$select = d3.select("#" + element)) === null || _d3$select === void 0 || (_d3$select = _d3$select.node()) === null || _d3$select === void 0 ? void 0 : _d3$select.getBoundingClientRect(); return boundingClientRect; } }; /* name: createAndAppendConnectorPath description: Creates a curved or angled (connector) path between two declared points */ var createAndAppendPath = exports.createAndAppendPath = function createAndAppendPath(svg, s, d, connectorPathType, xAxis, yAxis) { // var el = _this; var connector = null; // // original, curved connector ... if (connectorPathType == "curved") { connector = !xAxis || !yAxis ? connector = "M ".concat(s.x, " ").concat(s.y, "\n C ").concat((s.x + d.x) / 2, " ").concat(s.y, ",\n ").concat((s.x + d.x) / 2, " ").concat(d.y, ",\n ").concat(d.x, " ").concat(d.y) : connector = "M ".concat(xAxis(s.x), " ").concat(yAxis(s.y), "\n C ").concat((xAxis(s.x) + xAxis(d.x)) / 2, " ").concat(yAxis(s.y), ",\n ").concat((xAxis(s.x) + xAxis(d.x)) / 2, " ").concat(yAxis(d.y), ",\n ").concat(xAxis(d.x), " ").concat(yAxis(d.y)); } else { /* // // updated, angled connector ... if (connectorPathType == "angled") */ connector = !xAxis || !yAxis ? "M ".concat(s.x, " ").concat(s.y, "\n L ").concat((s.x + d.x) / 2, " ").concat(s.y, ", ").concat((s.x + d.x) / 2, " ").concat(d.y, "\n ").concat(d.x, " ").concat(d.y) : "M ".concat(xAxis(s.x), " ").concat(yAxis(s.y), "\n L ").concat((xAxis(s.x) + xAxis(d.x)) / 2, " ").concat(yAxis(s.y), ", ").concat((xAxis(s.x) + xAxis(d.x)) / 2, " ").concat(yAxis(d.y), "\n ").concat(xAxis(d.x), " ").concat(yAxis(d.y)); } var appendedConnector = d3.selectAll("." + svg.join(".") + "__group").append("path").attr("class", "chart__path__connector").attr("d", function (d, i) { return connector; }).style("fill", "none").style("stroke", "#FFFFFF").style("stroke-width", 2); return appendedConnector; }; // end method createAndAppendPath /* name: applyStrokeStyle description: */ var applyStrokeStyle = exports.applyStrokeStyle = function applyStrokeStyle(el, style) { el.attr("stroke", style.strokeColor).attr("stroke-width", style.strokeWidth ? style.strokeWidth : 0); }; // end method applyStrokeStyle /* name: getContainerHeight description: */ var getContainerHeight = exports.getContainerHeight = function getContainerHeight(containerWidth, heightToWidthRatio) { var height = containerWidth * heightToWidthRatio > 450 ? containerWidth * heightToWidthRatio : 450; return height; }; // end method getContainerHeight /* name: getContainerWidth description: */ var getContainerWidth = exports.getContainerWidth = function getContainerWidth(thisElement, numberOfColumns, margins) { var barSpace = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : 20; var barWidth = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : 30; var calculatedWidth = numberOfColumns * (barSpace + barWidth); var element = d3.select(thisElement).node(); //element.getBoundingClientRect().width return element && (calculatedWidth > element.getBoundingClientRect().width ? calculatedWidth : element.getBoundingClientRect().width); }; // end method getContainerWidth /* name: prototype.first() desc: */ d3.selection.prototype.first = function () { return d3.select(this[0][0]); }; /* name: prototype.last() desc: */ d3.selection.prototype.last = function () { var last = this.size() - 1; return d3.select(this[0][last]); }; /* name: prototype.moveToFront() desc: define prototype for move to front function */ d3.selection.prototype.moveToFront = function () { return this.each(function () { this.parentNode.appendChild(this); }); }; // end prototype moveToFront function /* name: prototype.moveToBack() desc: define prototype for move to back function */ d3.selection.prototype.moveToBack = function () { return this.each(function () { var firstChild = this.parentNode.firstChild; if (firstChild) { this.parentNode.insertBefore(this, firstChild); } }); }; // end prototype moveToBack function /* name: resetChart description: function to transition chart back to oroginsal default chart type view arguments: n/a returns: n/a calls:transitionStacked addGridlines called from: reset button */ var resetChart = exports.resetChart = function resetChart(xAxisScale, yAxisScale, rectBars, position_yAxis, position_xAxis, el, yStackMax, yGroupMax) { // // remove classes from data rectangles. d3.selectAll(".rectangle").classed("dataCategoryLayer-selected", false); d3.selectAll(".horizon").classed("dataCategoryLayer-selected", false); // update styling to correct form for onloadcharttype (defined in el.config.firstloadedChart) // remove 'chartType' class from all chart type selction buttons d3.selectAll(".chartType").classed("selected", false); // add 'chartType' class to newly selcted chart type selection button d3.selectAll(".chartType." + el.config.firstloadedChart).classed("selected", true); el.config.clickedChartType = el.config.firstloadedChart; // // call function to change chart type. changeChart(xAxisScale, yAxisScale, rectBars, /* svgPanel */el.config.svgPanel, position_yAxis, position_xAxis, el, yStackMax, yGroupMax, el.config.dataCategoryLength); // call function to add gridlines after chart redraw. var yAxisGridLines = appendYAxisGridLines(el.config.containerWidth - el.config.margins[el.config.screenSize].left - el.config.margins[el.config.screenSize].right, el.config.axis.y.gridLines, el.config.axis.y.classNames); return; }; // end function resetChart /* name: numberWithCommas description: function to add 1000s comma seperators to long numbers arguments: x - text string to modify returns: modified text string to display calls: none called from: drawArticlesOverTimeChart articles_mouseover */ var numberWithCommas = exports.numberWithCommas = function numberWithCommas(x) { if (x) { return x.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ","); } return ""; }; // end function numberWithCommas /* NAME: getScaleBandAxisScope DESCRIPTION: function called to ... */ var getScaleBandAxisScope = exports.getScaleBandAxisScope = function getScaleBandAxisScope(scaleBand) { // return { bandWidth: scaleBand.bandwidth(), step: scaleBand.step(), domain: scaleBand.domain(), range: scaleBand.range(), paddingInner: scaleBand.paddingInner(), paddingOuter: scaleBand.paddingOuter(), align: scaleBand.align(), round: scaleBand.round() }; // }; /* NAME: getScaleTimeAxisScope DESCRIPTION: function called to ... */ var getScaleTimeAxisScope = exports.getScaleTimeAxisScope = function getScaleTimeAxisScope(scaleTime) { // return { domain: scaleTime.domain(), range: scaleTime.range(), clamp: scaleTime.clamp() }; // }; // end method getScaleTimeAxisScope /* NAME: makeXAxisLabelAdaptations DESCRIPTION: function called to ... */ var makeXAxisLabelAdaptations = exports.makeXAxisLabelAdaptations = function makeXAxisLabelAdaptations(el, xAxisScope) { // if (xAxisScope.bandWidth < el.config.xaxisTickLabelsMaxWidth * 1.2) { var counter = 0; d3.selectAll(".x-axis").selectAll(".tick").style("display", function () { // if (el.config.screenSize == 6) { if (el.config.yearArrayLength > 75) { el.config.tickLabelFrequency = 4; } else if (el.config.yearArrayLength > 50) { el.config.tickLabelFrequency = 2; } else { el.config.tickLabelFrequency = 1; } } // else if (el.config.screenSize == 5) { if (el.config.yearArrayLength > 75) { el.config.tickLabelFrequency = 4; } else if (el.config.yearArrayLength > 40) { el.config.tickLabelFrequency = 2; } else { el.config.tickLabelFrequency = 1; } } // else if (el.config.screenSize == 4) { if (el.config.yearArrayLength > 75) { el.config.tickLabelFrequency = 4; } else if (el.config.yearArrayLength > 40) { el.config.tickLabelFrequency = 2; } else { el.config.tickLabelFrequency = 2; } } // else if (el.config.screenSize == 3) { if (el.config.yearArrayLength > 50) { el.config.tickLabelFrequency = 5; } else if (el.config.yearArrayLength > 20) { el.config.tickLabelFrequency = 2; } else { el.config.tickLabelFrequency = 1; } } // else if (el.config.screenSize == 2) { if (el.config.yearArrayLength > 55) { el.config.tickLabelFrequency = 5; } else if (el.config.yearArrayLength > 40) { el.config.tickLabelFrequency = 4; } else { el.config.tickLabelFrequency = 2; } } // else if (el.config.screenSize == 1) { if (el.config.yearArrayLength > 75) { el.config.tickLabelFrequency = 10; } else if (el.config.yearArrayLength > 55) { el.config.tickLabelFrequency = 10; } else if (el.config.yearArrayLength > 40) { el.config.tickLabelFrequency = 4; } else { el.config.tickLabelFrequency = 4; } } // else { console.log("catch all use case"); } return counter++ % el.config.tickLabelFrequency === 0 ? "inline" : "none"; }); } // update dimensions and positioning of x axis thick line ... d3.selectAll(".xAxisThickLine").attr("x1", el.config.margins[el.config.screenSize].left).attr("x2", el.config.containerWidth - el.config.margins[el.config.screenSize].right); return; }; // end function makeXAxisLabelAdaptations /* browser window screen widths and heights https://andylangton.co.uk/blog/development/get-viewportwindow-size-width-and-height-javascript http://ryanve.com/lab/dimensions/ */ var alertSize = exports.alertSize = function alertSize(el) { // /** this code will return the parent container dimensions if present */ var myWidth = document.querySelector("." + el.baseContainerDiv) ? document.querySelector("." + el.baseContainerDiv).clientWidth : document.documentElement.clientWidth; var myHeight = document.querySelector("." + el.baseContainerDiv) ? document.querySelector("." + el.baseContainerDiv).clientHeight : document.documentElement.clientHeight; el.config.width = myWidth; el.config.height = myHeight; return; }; // end function alertSize /* name: getMargins description: function to ... */ var getChartMargins = exports.getChartMargins = function getChartMargins(el) { // if (el.config.width <= 400) { el.config.screenSize = 0; } if (el.config.width > 400) { el.config.screenSize = 1; } if (el.config.width > 600) { el.config.screenSize = 2; } if (el.config.width > 800) { el.config.screenSize = 3; } if (el.config.width > 1000) { el.config.screenSize = 4; } if (el.config.width > 1200) { el.config.screenSize = 5; } if (el.config.width > 1400) { el.config.screenSize = 6; } return; }; // end function getMargins /* NAME: columnChartOnWindowResize DESCRIPTION: function called when user resizes window. handles updating of content reliant on dimension of window ARGUMENTS TAKEN: none ARGUMENTS RETURNED: none CALLED FROM: manual user resizing of browser window CALLS:alertSize addGridlines http://bl.ocks.org/johangithub/97a186c551e7f6587878 */ var columnChartOnWindowResize = exports.columnChartOnWindowResize = function columnChartOnWindowResize(el) { // el.config.containerWidth = getContainerWidth(el.element); // // // update width of all base SVG panels on which charts are built/appended ... d3.selectAll("." + el.config.svgBasePanelClassNames.join(".")).attr("width", el.config.containerWidth); // call methods to create x-axis // first get data to define axis domain var xAxisData = getColumnChartAxisData(el.config.data, el.config.xAxisDataKey); // define new scaleband x-axis var xAxisScale = createScaleBandXAxis(xAxisData, el.config.containerWidth - el.config.margins[el.config.screenSize].left - el.config.margins[el.config.screenSize].right); var xAxis = d3.axisBottom(xAxisScale); // update x numeric axis d3.selectAll(".x-axis").call(xAxis); // append full width gridlines to chart y-axis var yAxisGridLines = appendYAxisGridLines(el.config.containerWidth - el.config.margins[el.config.screenSize].left - el.config.margins[el.config.screenSize].right, el.config.axis.y.gridLines, el.config.axis.y.classNames); // transition new data rectangle to groups d3.selectAll(".horizon").selectAll(".rectangle").attr("x", function (d, i) { return xAxisScale(i); }).attr("width", xAxisScale.bandwidth()); // update legend position d3.selectAll(".container__chart__legend").attr("transform", "translate(" + Number(el.config.containerWidth - el.config.margins[el.config.screenSize].right - 20) + "," + 75 + ")"); // initialise and append x-axis main title label d3.selectAll(".title__x-axis").attr("x", (el.config.containerWidth - d3.select(".title__x-axis").node().getBBox().width) / 2); var counter = 0; d3.selectAll(".x-axis").selectAll(".tick").attr("class", function () { // add additional classes to x-axis tick labels... // and update tick label text d3.select(this).selectAll("text").attr("class", function (d, i) { return "tick__label xAxisTickLabels tick__label-" + counter; }).style("display", "inline").text(el.config.arrayOfYears[counter]); // add additional classes to x-axis tick lines... d3.select(this).selectAll("line").attr("class", function () { return "tick__tick-line tick__tick-line-" + counter; }); return "tick tick-" + counter++; }); // determine longest text string on x-axis tick labels ... el.config.xaxisTickLabelsMaxWidth = d3.max(d3.selectAll(".xAxisTickLabels").nodes(), function (n) { return n.getComputedTextLength(); }); // determine length of x-axis el.config.xAxisLength = xAxisScale.range()[1] - xAxisScale.range()[0]; // determine pixel distance between successive tick marks. // store client rect for first tick line on x axis el.config.firstTickBoundingClientRect = d3.selectAll(".tick__tick-line-0").node().getBoundingClientRect(); // store client rect for second tick line on x axis el.config.secondTickBoundingClientRect = d3.selectAll(".tick__tick-line-1").node().getBoundingClientRect(); el.config.tickMarkPixelDistance = Number(el.config.secondTickBoundingClientRect.x - el.config.firstTickBoundingClientRect.x); var xAxisScaleBandScope = getScaleBandAxisScope(xAxisScale); el.config.chartWidth = el.config.containerWidth - el.config.margins[el.config.screenSize].left - el.config.margins[el.config.screenSize].right; makeXAxisLabelAdaptations(el, xAxisScaleBandScope); return; }; // end function columnChartOnWindowResize /* METHODS LISTED BELOW THIS POINT ARE SPECIFIC TO AN ANIMATED MULTILAYER COLUMN CHART */ /* name: getDataToDrawColumnChartCategories desc: */ var getDataToDrawColumnChartCategories = exports.getDataToDrawColumnChartCategories = function getDataToDrawColumnChartCategories(data) { // var listOfDataKeys = Object.keys(data[0]); // var categoryData = data.map(function (item) { return listOfDataKeys.map(function (key) { return item[key]; }); }); return categoryData; }; // end method getDataToDrawColumnChartCategories /* name: changeChart description: function to handle user selecting a different chart style to visualise data with arguments: e - reference to button/element clicked returns: none calls:transitionGrouped transitionStacked transitionPercent addGridlines removeStickyTooltip called from: drawArticlesOverTimeChart */ var changeChart = exports.changeChart = function changeChart(/* e, */ xAxisScale, yAxisScale, rectBars, svg, position_yAxis, position_xAxis, el, yStackMax, yGroupMax, dataCategoryLength) { if (el.config.clickedChartType === "grouped") { (0, _columnTransition.transitionGrouped)(xAxisScale, yAxisScale, rectBars, svg, position_yAxis, el, yGroupMax, dataCategoryLength); } if (el.config.clickedChartType === "stacked") { (0, _columnTransition.transitionStacked)(xAxisScale, yAxisScale, rectBars, svg, position_yAxis, el, yStackMax); } if (el.config.clickedChartType === "percent") { (0, _columnTransition.transitionPercent)(xAxisScale, yAxisScale, rectBars, svg, position_yAxis, el); } // call function to add gridlines after chart redraw. appendYAxisGridLines(el.config.containerWidth - el.config.margins[el.config.screenSize].left - el.config.margins[el.config.screenSize].right, el.config.axis.y.gridLines, el.config.axis.y.classNames); // remove 'chartType' class from all chart type selction buttons d3.selectAll(".chartType").classed("selected", false); // add 'chartType' class to newly selcted chart type selection button d3.selectAll(".chartType." + el.config.clickedChartType).classed("selected", true); }; // end function changeChart /* name: getStackMax desc: get data maximum of when data bars are in stacked chart layout */ var getStackMax = exports.getStackMax = function getStackMax(data) { // return Math.ceil(d3.max(data, function (layer) { return d3.max(layer, function (d) { return d[1]; }); })); }; // end method getStackMax /* name: getGroupMax desc: get data maximum of when data bars are in grouped chart layout */ var getGroupMax = exports.getGroupMax = function getGroupMax(data) { // return Math.ceil(d3.max(data, function (layer) { return d3.max(layer, function (d) { return d[1] - d[0]; }); })); }; // end method getGroupMax /* name: generateLayersForColumnChart desc: create base group elements to contain individual data horizons on chart */ var generateLayersForColumnChart = exports.generateLayersForColumnChart = function generateLayersForColumnChart(data, dataCategoryLength) { return d3.stack().keys(d3.range(dataCategoryLength))(data); }; // end method generateLayerGroupsForColumnChart /* name: generateLayerGroupsForColumnChart desc: create base group elements to contain individual data horizons on chart */ var generateLayerGroupsForColumnChart = exports.generateLayerGroupsForColumnChart = function generateLayerGroupsForColumnChart(svg, dataLayers, colours) { return svg.selectAll(".layer").data(dataLayers).enter().append("g").attr("class", function (_, i) { return "layer-".concat(i, " horizon"); }).attr("id", function (d) { return d.key; }).style("fill", function (_, i) { return colours[i]; }); }; // end method generateLayerGroupsForColumnChart /* name: generateBarsForColumnChart desc: append data rectangles to chart */ var generateBarsForColumnChart = exports.generateBarsForColumnChart = function generateBarsForColumnChart(dataLayer, height, axisX) { // return dataLayer.selectAll("rect").data(function (d) { return d; }).enter().append("rect").attr("class", function (d, i) { var pc = this.parentNode.className.baseVal.replace("layer ", "layer-").replace("horizon", ""); return "".concat(pc, "rectangle stack-").concat(i); }).attr("id", function (d, i) { var pc = this.parentNode.className.baseVal.replace("layer ", "layer-").replace("horizon", ""); return (pc + "-rect_" + i).replaceAll(" ", ""); }).attr("x", function (d, i) { return axisX(i); }).attr("y", height).attr("width", axisX.bandwidth()).attr("height", 0); }; // end method generateBarsForColumnChart var applyTheme = exports.applyTheme = function applyTheme(theme) { var _document$body$classL; var validThemes = ["dark", "light", "custom"]; (_document$body$classL = document.body.classList).remove.apply(_document$body$classL, validThemes); document.body.classList.add(theme); }; var clearSvgBasePanel = exports.clearSvgBasePanel = function clearSvgBasePanel(svgClassName) { return d3.select(".".concat(svgClassName)).selectAll("*").remove(); }; /* name: mouseover description: function to handle user mouseover interaction on chart arguments: returns: calls: called from: */ var mouseover = exports.mouseover = function mouseover(element, i, d, xAxisData, el) { el.config.hoverLabelIndex = /* this */element.classList[0].split("-")[1]; var hoverLabelIndex = element.classList[0].split("-")[1]; // // localise storage of data year for data column moused over by user var year = xAxisData[i]; var tootipData = { year: year, data: d.data }; // // localy store relevant dimensions of selected rectangle var rectX = Number(d3.select(element).attr("x")); var rectY = Number(d3.select(element).attr("y")); var rectWidth = Number(d3.select(element).attr("width")) + 10; var visWidth = el.config.width; var tootipElement = (0, _tooltips.createTooltip)(el.config, tootipData, hoverLabelIndex); var x = rectX > visWidth / 2 ? rectX - rectWidth / 2 - document.querySelector(".tooltip-group").clientWidth / 2 : rectX + rectWidth / 3 + document.querySelector(".tooltip-group").clientWidth / 2; tootipElement.setAttribute("style", "top: ".concat(rectY, "px; left: ").concat(x, "px;")); }; // end function mouseover /* name: mouseleave description: The function that change the tooltip when user leave a rectangle arguments: element - element selected d - d3 data var i - d3 data index var returns: none calls: none called from: drawArticlesOverTimeChart */ var mouseleave = exports.mouseleave = function mouseleave(element, el) { // initialise and update index of mouseover'd rect el.config.hoverLabelIndex = null; // the user has just mouseleave'd a data category horizon, ... , but that data category horizon is NOT currently selected by the user ... if (el.config.isLayerSelected == null) { d3.selectAll(".tooltip-group:not(.clickedToCompare)").remove(); // // remove all styling from all swatches ... d3.selectAll(".rectangle").classed("layerHighlightColour", false); } // end if ... // // the user has just mouseleave'd a data category horizon, ... , and a different data category horizon is currently selected by the user ... else if (el.config.isLayerSelected != el.config.selectedLayer) { d3.selectAll(".tooltip-group:not(.clickedToCompare)").remove(); // remove all styling from rectangle blocks on selected layer d3.selectAll(".rectangle." + el.config.selectedLayer).classed("layerHighlightColour", false); // remove all styling from selected rectangle block d3.select(element).classed("chart__rectangle--highlighted", false); } // end else if ... // // the user has just mouseleave'd a data category horizon, ... , and that data category horizon is currently selected by the user ..., else { d3.selectAll(".tooltip-group:not(.clickedToCompare)").remove(); d3.selectAll(".pearl-div-tooltip").remove(); // // remove all styling from all swatches ... d3.selectAll(".rectangle").classed("layerHighlightColour", false); // // add lighter styling to all rectangle blocks on selected data horizon d3.selectAll(".rectangle." + el.config.selectedLayer).classed("layerHighlightColour", true); // // add darker highlighting styling from selected rectangle block d3.select(element).classed("chart__rectangle--highlighted", true); } // end else ... return; }; // end function mouseleave /* name: clickChart description: function called when user clicks on chart to permanently highlight/style a data horizon arguments: none returns: none calls: none called from: drawArticlesOverTimeChart */ var clickChart = exports.clickChart = function clickChart(el) { // activates chart reset button ... document.getElementById("resetChart").disabled = false; el.config.isPreviousLayerSelected = el.config.isLayerSelected; el.config.isLayerSelected = el.config.isLayerSelected == el.config.selectedLayer ? null : el.config.selectedLayer; }; // end function clickChart var rgbToHex = exports.rgbToHex = function rgbToHex(rgbString) { var rgbSplits = rgbString.split("(")[1].split(")")[0]; var rgbNumbers = rgbSplits.split(","); var hexBase64Splits = rgbNumbers.map(numberToHexValue); var hexString = "#".concat(hexBase64Splits.join("")); return hexString; }; var numberToHexValue = function numberToHexValue(num) { num = parseInt(num).toString(16); // Convert to a base16 string num = num.length == 1 ? "0".concat(num) : num; return num.toUpperCase(); }; var updateNestedProperties = exports.updateNestedProperties = function updateNestedProperties(source, updates) { var updatedSource = _objectSpread({}, source); Object.entries(updates).forEach(function (_ref) { var _ref2 = _slicedToArray(_ref, 2), key = _ref2[0], value = _ref2[1]; updatedSource[key] = _objectSpread(_objectSpread({}, source[key]), value); }); return updatedSource; }; var appendElementToTarget = exports.appendElementToTarget = function appendElementToTarget(element, target) { return target.appendChild(element); };