1
0
Fork 0
mirror of https://github.com/maybe-finance/maybe.git synced 2025-07-27 09:09:41 +02:00

Refactor trendline chart controller into a time series chart controller

This commit is contained in:
Jose Farias 2024-04-19 09:50:15 -06:00
parent ee920f359c
commit 237cfc1f45
3 changed files with 139 additions and 6 deletions

View file

@ -0,0 +1,135 @@
import { Controller } from "@hotwired/stimulus"
import tailwindColors from "@maybe/tailwindcolors"
import * as d3 from "d3"
export default class extends Controller {
static values = { series: Object }
#_dataPoints = []
#_d3Svg = null
connect() {
this.#install()
document.addEventListener("turbo:load", this.#reinstall)
}
disconnect() {
document.removeEventListener("turbo:load", this.#reinstall)
}
#reinstall = () => {
this.#teardown()
this.#install()
}
#teardown() {
this.#_d3Svg = null
this.#_dataPoints = []
this.#d3Container.selectAll("*").remove()
}
#install() {
this.#normalizeDataPoints()
this.#draw()
}
#normalizeDataPoints() {
this.#dataPoints = this.seriesValue.values.map((d) => ({
date: new Date(d.date),
value: d.value.amount ? +d.value.amount : +d.value,
}))
}
#draw() {
this.#d3Svg
.append("path")
.datum(this.#dataPoints)
.attr("fill", "none")
.attr("stroke", this.#trendColor)
.attr("stroke-width", 2)
.attr("d", this.#d3Line);
}
#createD3Svg() {
const height = this.#d3ContainerHeight
const width = this.#d3ContainerWidth
return this.#d3Container
.append("svg")
.attr("width", width)
.attr("height", height)
.attr("viewBox", [ 0, 0, width, height ])
}
get #dataPoints() {
return this.#_dataPoints
}
set #dataPoints(dataPoints) {
this.#_dataPoints = dataPoints
}
get #d3Svg() {
if (this.#_d3Svg) {
return this.#_d3Svg
} else {
return this.#_d3Svg = this.#createD3Svg()
}
}
get #d3ContainerWidth() {
return this.#d3Container.node().clientWidth
}
get #d3ContainerHeight() {
return this.#d3Container.node().clientHeight
}
get #d3Container() {
return d3.select(this.element)
}
get #trendColor() {
if (this.#trendDirection === "flat") {
return tailwindColors.gray[500]
} else if (this.#trendDirection === this.#favorableDirection) {
return tailwindColors.green[500]
} else {
return tailwindColors.error
}
}
get #trendDirection() {
return this.seriesValue.trend.direction
}
get #favorableDirection() {
return this.seriesValue.trend.favorableDirection
}
get #d3Line() {
return d3
.line()
.x(d => this.#d3XScale(d.date))
.y(d => this.#d3YScale(d.value))
}
get #d3XScale() {
return d3
.scaleTime()
.rangeRound([0, this.#d3ContainerWidth])
.domain(d3.extent(this.#dataPoints, d => d.date))
}
get #d3YScale() {
const PADDING = 0.05
const dataMin = d3.min(this.#dataPoints, d => d.value)
const dataMax = d3.max(this.#dataPoints, d => d.value)
const padding = (dataMax - dataMin) * PADDING
return d3
.scaleLinear()
.rangeRound([this.#d3ContainerHeight, 0])
.domain([dataMin - padding, dataMax + padding])
}
}