charts/stackedarea.mjs (210 lines of code) (raw):
import dataTools from "./shared/dataTools"
import ColorScale from "./shared/colorscale"
import { getMargins, numberFormat, mustache, mobileCheck, stackMin, stackMax, textPadding, textPaddingMobile, getLabelFromColumn } from './shared/toolbelt';
import { addLabel, clickLogging } from './shared/arrows'
import Dropdown from "./shared/dropdown";
import { addDrops } from "./shared/drops"
import Tooltip from "./shared/tooltip"
import { addPeriods } from "./shared/periods"
import { addLabels } from "./shared/labels"
export default class Stackedarea {
constructor(settings) {
this.settings = settings
this.init()
}
init() {
if (this.settings.tooltip != "") {
this.tooltip = new Tooltip(this.settings.tooltip)
}
if (this.settings.dropdown.length > 0) {
addDrops()
this.dropdown = new Dropdown(
"dataPicker",
dataTools.getDropdown(this.settings.dropdown, this.settings.keys)
)
this.dropdown.on("dropdown-change", (label) => {
let data = this.settings.dropdown.find(d => d.label == label)
this.settings.stackedbars = data.values.split(',').map(d => d.trim())
if (this.settings.tooltip != "") {
this.tooltip.updateTemplate(data.tooltip)
}
this.render()
})
}
this.render()
}
render() {
let { modules,
height,
width,
isMobile,
colors,
datum,
title,
subtitle,
source,
dateFormat,
yScaleType,
xAxisLabel,
yAxisLabel,
minY,
maxY,
periodDateFormat,
marginleft,
margintop,
marginbottom,
marginright,
tooltip,
keys,
data,
labels,
userkey,
lines,
periods,
type,
aria,
colorScheme,
dropdown,
parseTime,
timeInterval,
xAxisDateFormat,
xColumn,
stackedbars,
columns } = this.settings
d3.select("#graphicContainer svg").remove()
const chartKey = d3.select("#chartKey")
chartKey.html("")
datum = JSON.parse(JSON.stringify(data))
colors = new ColorScale()
isMobile = mobileCheck()
width = document
.querySelector("#graphicContainer")
.getBoundingClientRect().width
height = width * 0.5
const keyColor = dataTools.getKeysColors({ keys: stackedbars, userKey: userkey, option: { colorScheme : colorScheme }})
colors.set(keyColor.keys, keyColor.colors)
stackedbars.forEach((key, i) => {
const keyDiv = chartKey
.append("div")
.attr("class", "keyDiv")
keyDiv
.append("span")
.attr("class", "keyCircle")
.style("background-color", () => colors.get(key))
keyDiv
.append("span")
.attr("class", "keyText")
.text(getLabelFromColumn(columns, key))
})
datum.forEach((d) => {
if (parseTime != null) {
if (typeof d[xColumn] === "string") {
d[xColumn] = parseTime(d[xColumn])
}
}
stackedbars.forEach((key, i) => {
d[key] = (d[key] != null) ? +d[key] : null
})
d.Total = d3.sum(stackedbars, (k) => +d[k])
})
labels.forEach((d) => {
if (parseTime != null) {
d.x1 = parseTime(d.x1)
}
d.y1 = +d.y1
d.y2 = +d.y2
})
const layers = d3.stack()
.keys(stackedbars)(datum)
layers.forEach(function (layer) {
layer.forEach(function (subLayer) {
subLayer.group = layer.key
subLayer.groupValue = subLayer.data[layer.key]
subLayer.total = subLayer.data.Total
})
})
width = width - marginleft - marginright
height = height - margintop - marginbottom
const svg = d3
.select("#graphicContainer")
.append("svg")
.attr("width", width + marginleft + marginright)
.attr("height", height + margintop + marginbottom)
.attr("id", "svg")
.attr("overflow", "hidden")
const features = svg
.append("g")
.attr("transform", "translate(" + marginleft + "," + margintop + ")")
const x = d3.scaleTime()
.range([0, width])
x.domain(d3.extent(datum, d => d[xColumn]))
const y = d3.scaleLinear()
.range([height, 0])
y.domain([d3.min(layers, stackMin), d3.max(layers, stackMax)])
var area = d3.area()
.x((d, i) => x(d.data[xColumn]))
.y0((d) => y(d[0]))
.y1((d) => y(d[1]));
var xAxis
var yAxis
var ticks = 3
if (isMobile) {
xAxis = d3.axisBottom(x)
.tickFormat(xAxisDateFormat)
.ticks(4)
yAxis = d3
.axisLeft(y)
.tickFormat((d) => numberFormat(d))
.ticks(5)
} else {
xAxis = d3.axisBottom(x)
.tickFormat(xAxisDateFormat)
yAxis = d3.axisLeft(y)
.tickFormat((d) => numberFormat(d))
}
features.append("g").attr("class", "y").call(yAxis)
d3.selectAll(".y .tick line")
.style("stroke", "#dcdcdc")
.style("stroke-dasharray", "2 2")
.attr("x2", width)
d3.selectAll(".y path")
.style("stroke-width", "0")
const layer = features
.selectAll("layer")
.data(layers, (d) => d.key)
.enter()
.append("g")
.attr("class", (d) => "layer " + d.key)
.style("fill", (d, i) => colors.get(d.key))
layer.append("path")
.attr("class", "area")
.attr("d", area);
features
.append("g")
.attr("class", "x")
.attr("transform", "translate(0," + height + ")")
.call(xAxis)
if (xAxisLabel) {
features
.append("text")
.attr("x", width)
.attr("y", height + (marginbottom / 2))
.attr("fill", "#767676")
.attr("text-anchor", "end")
.text(xAxisLabel)
}
if (yAxisLabel) {
features
.append("text")
.attr("transform", "rotate(-90)")
.attr("y", 6)
.attr("dy", "0.71em")
.attr("fill", "#767676")
.attr("text-anchor", "end")
.text(yAxisLabel)
}
labels.forEach((config) => {
addLabel(svg, config, width + marginleft + marginright, height + margintop + marginbottom,{ top: margintop,right: marginright, bottom: marginbottom, left: marginleft }, false)
})
}
}