Merge pull request #897 from ubavic/fix/cashin_scatterplot
fix: scatter plot
This commit is contained in:
commit
22fb26f81c
1 changed files with 43 additions and 57 deletions
|
|
@ -1,20 +1,17 @@
|
||||||
import * as d3 from 'd3'
|
import * as d3 from 'd3'
|
||||||
import moment from 'moment'
|
import moment from 'moment'
|
||||||
import * as R from 'ramda'
|
|
||||||
import React, { useEffect, useRef, useCallback } from 'react'
|
import React, { useEffect, useRef, useCallback } from 'react'
|
||||||
|
|
||||||
import { backgroundColor, java, neon } from 'src/styling/variables'
|
import { backgroundColor, java, neon } from 'src/styling/variables'
|
||||||
|
|
||||||
const RefScatterplot = ({ data: realData, timeFrame, timezone }) => {
|
const RefScatterplot = ({ data: realData, timeFrame, timezone }) => {
|
||||||
const svgRef = useRef()
|
const svgRef = useRef()
|
||||||
const cashIns = R.filter(R.propEq('txClass', 'cashIn'))(realData)
|
|
||||||
const cashOuts = R.filter(R.propEq('txClass', 'cashOut'))(realData)
|
|
||||||
const drawGraph = useCallback(() => {
|
const drawGraph = useCallback(() => {
|
||||||
const svg = d3.select(svgRef.current)
|
const svg = d3.select(svgRef.current)
|
||||||
const margin = { top: 25, right: 0, bottom: 25, left: 15 }
|
const margin = { top: 25, right: 0, bottom: 25, left: 15 }
|
||||||
const width = 555 - margin.left - margin.right
|
const width = 555 - margin.left - margin.right
|
||||||
const height = 150 - margin.top - margin.bottom
|
const height = 150 - margin.top - margin.bottom
|
||||||
|
const dstOffset = parseInt(timezone.split(':')[1])
|
||||||
// finds maximum value for the Y axis. Minimum value is 100. If value is multiple of 1000, add 100
|
// finds maximum value for the Y axis. Minimum value is 100. If value is multiple of 1000, add 100
|
||||||
// (this is because the Y axis looks best with multiples of 100)
|
// (this is because the Y axis looks best with multiples of 100)
|
||||||
const findMaxY = () => {
|
const findMaxY = () => {
|
||||||
|
|
@ -26,23 +23,28 @@ const RefScatterplot = ({ data: realData, timeFrame, timezone }) => {
|
||||||
return maxY
|
return maxY
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const timeFormat = v => {
|
||||||
|
switch (timeFrame) {
|
||||||
|
case 'Week':
|
||||||
|
return d3.timeFormat('%a %d')(v)
|
||||||
|
case 'Month':
|
||||||
|
return d3.timeFormat('%b %d')(v)
|
||||||
|
default:
|
||||||
|
return moment
|
||||||
|
.utc(v)
|
||||||
|
.add(dstOffset, 'minutes')
|
||||||
|
.format('HH:mm')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// changes values of arguments in some d3 function calls to make the graph labels look good according to the selected time frame
|
// changes values of arguments in some d3 function calls to make the graph labels look good according to the selected time frame
|
||||||
const findXAxisSettings = () => {
|
const findXAxisSettings = () => {
|
||||||
// case 'Day' or default
|
|
||||||
const res = {
|
|
||||||
nice: null,
|
|
||||||
ticks: 4,
|
|
||||||
subtractDays: 1,
|
|
||||||
timeFormat: '%H:%M',
|
|
||||||
timeRange: [50, 500]
|
|
||||||
}
|
|
||||||
switch (timeFrame) {
|
switch (timeFrame) {
|
||||||
case 'Week':
|
case 'Week':
|
||||||
return {
|
return {
|
||||||
nice: 7,
|
nice: 7,
|
||||||
ticks: 7,
|
ticks: 7,
|
||||||
subtractDays: 7,
|
subtractDays: 7,
|
||||||
timeFormat: '%a %d',
|
|
||||||
timeRange: [50, 500]
|
timeRange: [50, 500]
|
||||||
}
|
}
|
||||||
case 'Month':
|
case 'Month':
|
||||||
|
|
@ -50,11 +52,15 @@ const RefScatterplot = ({ data: realData, timeFrame, timezone }) => {
|
||||||
nice: 6,
|
nice: 6,
|
||||||
ticks: 6,
|
ticks: 6,
|
||||||
subtractDays: 30,
|
subtractDays: 30,
|
||||||
timeFormat: '%b %d',
|
|
||||||
timeRange: [50, 500]
|
timeRange: [50, 500]
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
return res
|
return {
|
||||||
|
nice: null,
|
||||||
|
ticks: 4,
|
||||||
|
subtractDays: 1,
|
||||||
|
timeRange: [50, 500]
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -65,11 +71,10 @@ const RefScatterplot = ({ data: realData, timeFrame, timezone }) => {
|
||||||
svg
|
svg
|
||||||
.append('rect')
|
.append('rect')
|
||||||
.attr('x', 0)
|
.attr('x', 0)
|
||||||
.attr('y', -margin.top)
|
.attr('y', 0)
|
||||||
.attr('width', width + margin.left + margin.right)
|
.attr('width', width)
|
||||||
.attr('height', height + margin.top)
|
.attr('height', height + margin.top)
|
||||||
.attr('fill', backgroundColor)
|
.attr('fill', backgroundColor)
|
||||||
.attr('transform', `translate(${0},${margin.top})`)
|
|
||||||
|
|
||||||
// declare g variable where more svg components will be attached
|
// declare g variable where more svg components will be attached
|
||||||
const g = svg
|
const g = svg
|
||||||
|
|
@ -92,15 +97,17 @@ const RefScatterplot = ({ data: realData, timeFrame, timezone }) => {
|
||||||
.domain([
|
.domain([
|
||||||
moment()
|
moment()
|
||||||
.add(-xAxisSettings.subtractDays, 'day')
|
.add(-xAxisSettings.subtractDays, 'day')
|
||||||
.add(timezone.dstOffset, 'minutes')
|
|
||||||
.valueOf(),
|
.valueOf(),
|
||||||
moment()
|
moment().valueOf()
|
||||||
.add(timezone.dstOffset, 'minutes')
|
|
||||||
.valueOf()
|
|
||||||
])
|
])
|
||||||
.range(xAxisSettings.timeRange)
|
.range(xAxisSettings.timeRange)
|
||||||
.nice(xAxisSettings.nice)
|
.nice(xAxisSettings.nice)
|
||||||
|
|
||||||
|
const timeValue = s => {
|
||||||
|
const date = moment.utc(s)
|
||||||
|
return x(date.valueOf())
|
||||||
|
}
|
||||||
|
|
||||||
// horizontal gridlines
|
// horizontal gridlines
|
||||||
const makeYGridlines = () => {
|
const makeYGridlines = () => {
|
||||||
return d3.axisLeft(y).ticks(4)
|
return d3.axisLeft(y).ticks(4)
|
||||||
|
|
@ -127,7 +134,7 @@ const RefScatterplot = ({ data: realData, timeFrame, timezone }) => {
|
||||||
.axisBottom(x)
|
.axisBottom(x)
|
||||||
.ticks(xAxisSettings.ticks)
|
.ticks(xAxisSettings.ticks)
|
||||||
.tickSize(0)
|
.tickSize(0)
|
||||||
.tickFormat(d3.timeFormat(xAxisSettings.timeFormat))
|
.tickFormat(timeFormat)
|
||||||
)
|
)
|
||||||
.selectAll('text')
|
.selectAll('text')
|
||||||
.attr('dy', '1.5em')
|
.attr('dy', '1.5em')
|
||||||
|
|
@ -144,7 +151,6 @@ const RefScatterplot = ({ data: realData, timeFrame, timezone }) => {
|
||||||
)
|
)
|
||||||
.selectAll('text')
|
.selectAll('text')
|
||||||
.attr('dy', '1.5em')
|
.attr('dy', '1.5em')
|
||||||
/* ******************** */
|
|
||||||
|
|
||||||
// Y axis
|
// Y axis
|
||||||
g.append('g')
|
g.append('g')
|
||||||
|
|
@ -163,41 +169,21 @@ const RefScatterplot = ({ data: realData, timeFrame, timezone }) => {
|
||||||
.attr('dy', '-0.40em')
|
.attr('dy', '-0.40em')
|
||||||
.attr('dx', '3em')
|
.attr('dx', '3em')
|
||||||
|
|
||||||
/* APPEND DOTS */
|
// Append dots
|
||||||
svg
|
const dots = svg
|
||||||
.append('g')
|
.append('g')
|
||||||
.selectAll('dot')
|
.attr('transform', `translate(${margin.left},${margin.top})`)
|
||||||
.data(cashIns)
|
|
||||||
.enter()
|
|
||||||
.append('circle')
|
|
||||||
.attr('cx', function(d) {
|
|
||||||
const date = new Date(d.created)
|
|
||||||
return x(date.setMinutes(date.getMinutes() + timezone.dstOffset))
|
|
||||||
})
|
|
||||||
.attr('cy', function(d) {
|
|
||||||
return y(d.fiat)
|
|
||||||
})
|
|
||||||
.attr('r', 4)
|
|
||||||
.attr('transform', 'translate(' + margin.left + ',' + 15 + ')')
|
|
||||||
.style('fill', java)
|
|
||||||
svg
|
|
||||||
.append('g')
|
|
||||||
.selectAll('dot')
|
|
||||||
.data(cashOuts)
|
|
||||||
.enter()
|
|
||||||
.append('circle')
|
|
||||||
.attr('cx', function(d) {
|
|
||||||
return x(new Date(d.created))
|
|
||||||
})
|
|
||||||
.attr('cy', function(d) {
|
|
||||||
return y(d.fiat)
|
|
||||||
})
|
|
||||||
.attr('r', 4)
|
|
||||||
.attr('transform', 'translate(' + margin.left + ',' + 15 + ')')
|
|
||||||
.style('fill', neon)
|
|
||||||
|
|
||||||
/* ************************** */
|
dots
|
||||||
}, [cashIns, cashOuts, realData, timeFrame, timezone])
|
.selectAll('circle')
|
||||||
|
.data(realData)
|
||||||
|
.enter()
|
||||||
|
.append('circle')
|
||||||
|
.attr('cx', d => timeValue(d.created))
|
||||||
|
.attr('cy', d => y(d.fiat))
|
||||||
|
.attr('r', 4)
|
||||||
|
.style('fill', d => (d.txClass === 'cashIn' ? java : neon))
|
||||||
|
}, [realData, timeFrame, timezone])
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
// first we clear old chart DOM elements on component update
|
// first we clear old chart DOM elements on component update
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue