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:
parent
ee920f359c
commit
237cfc1f45
3 changed files with 139 additions and 6 deletions
135
app/javascript/controllers/time_series_chart_controller.js
Normal file
135
app/javascript/controllers/time_series_chart_controller.js
Normal 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])
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue