@springernature/nn-charts
Version:
Visualization for DAS products
830 lines (765 loc) • 36.7 kB
JavaScript
;
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);
};