/* eslint-disable require-jsdoc, id-length, no-undefined, no-unused-vars, multiline-ternary, no-ternary, no-nested-ternary, no-invalid-this */ /* eslint prefer-reflect: ["error", { "exceptions": ["call"] }] */ /* eslint dot-location: ["error", "property"] */ /* eslint no-unused-vars: 0 */ import { select, mouse } from "d3-selection"; import { min, max, extent, ascending, bisector } from "d3-array"; import { scaleTime, scaleLinear } from "d3-scale"; import { axisLeft, axisBottom } from "d3-axis"; import { timeMonth } from "d3-time"; import { timeFormat, isoParse } from "d3-time-format"; import { area, line } from "d3-shape"; // lib export default function areachart(opts = {}) { const parseData = (data) => { // format the data data.forEach((d) => { d.key = isoParse(d.key) d.value = Number(d.value) }); // order by date return data.sort((x, y) => ascending(x.key, y.key)) } // OPTIONAL: Helper function to accumulates all data values const aggregate = (agg) => agg.map((item, index, array) => { if (index > 0) { item.value += array[index - 1].value } return item }) // parse opts let data = parseData(opts.data) let title = opts.title let objectName = opts.objectName || "" let container = select(opts.container) let showAxis = opts.axis || false let ratio = (opts.ratio || "").split(":").reduce((a, b) => a / b) || (4 / 3) let showTooltip = Object.is(opts.tip, undefined) ? true : opts.tip let cumulative = opts.agg || false if (cumulative) { data = aggregate(data) } // set the dimensions and margins of the graph let margin = { top: 0, right: 0, bottom: 0, left: 0 } let width = Number(container.node().getBoundingClientRect().width) - margin.left - margin.right let height = (width / ratio) - margin.top - margin.bottom let titlePadding = min([width / 10, 32]) // set the ranges let x = scaleTime().range([0, width]); let y = scaleLinear().range([height, 0]); // define the area let _area = area() .x((d) => x(d.key)) .y0(height) .y1((d) => y(d.value)); // define the line let valueline = line() .x((d) => x(d.key)) .y((d) => y(d.value)); let svg = container.append("svg") .attr("width", width + margin.left + margin.right) .attr("height", height + margin.top + margin.bottom) .append("g") .attr("transform", `translate(${margin.left},${margin.top})`); // scale the range of the data x.domain(extent(data, (d) => d.key)); y.domain([0, max(data, (d) => d.value)]).nice(); // add the valueline path. let topLine = svg.append("path") .data([data]) .attr("class", "line") .attr("d", valueline) // add the area svg.append("path") .data([data]) .attr("class", "area") .attr("d", _area) if (showTooltip) { // tooltip let circle = svg.append("circle") .attr("class", "circle") .attr("r", 6) .style("display", "none") let tooltip = select("body").append("div") .attr("id", `${container.node().id}-tooltip`) .attr("class", "chart-tooltip") .style("opacity", 0) svg .on("mouseover", () => { circle.style("display", null) tooltip.style("opacity", 1) }) .on("mouseout", () => { circle.style("display", "none") tooltip.style("opacity", 0) }) .on("mousemove", function() { let x0 = x.invert(mouse(this)[0]) let i = bisector((d) => d.key).left(data, x0, 1) let d0 = data[i - 1] let d1 = data[i] let d = (x0 - d0.key > d1.key - x0) ? d1 : d0 // svg position relative to document let coords = { x: window.pageXOffset + container.node().getBoundingClientRect().left, y: window.pageYOffset + container.node().getBoundingClientRect().top } let tooltipContent = `