chart updates

This commit is contained in:
Alex Bates 2021-01-07 14:38:30 +00:00
parent ec241dcd38
commit a59e59458e
No known key found for this signature in database
GPG Key ID: 7531C5E1D6B1CA9A
5 changed files with 65 additions and 202 deletions

View File

@ -8,8 +8,7 @@
"clsx": "^1.1.1",
"react": "^17.0.1",
"react-dom": "^17.0.1",
"recharts": "^2.0.0",
"use-element-dimensions": "^2.1.3"
"recharts": "^2.0.0"
},
"devDependencies": {
"parcel-bundler": "^1.12.4",

View File

@ -1,7 +1,10 @@
import React, { useState, useEffect } from "react"
import React, { useState, useEffect, useRef } from "react"
import { createPortal } from "react-dom"
import { Area, XAxis, YAxis, AreaChart, CartesianGrid, Tooltip } from "recharts"
import useDimensions from "use-element-dimensions"
import { Area, XAxis, YAxis, AreaChart, CartesianGrid, Tooltip, ResponsiveContainer } from "recharts"
import { scalePow } from "d3-scale"
const scale = scalePow()
.exponent(30)
const csvVersions = {
"1": {
@ -38,38 +41,42 @@ async function fetchData() {
})
}
export default function ProgressPane({ captionPortal }) {
const [data, setData] = useState(null)
let cachedData = null
export default function ProgressPane({ captionPortal, nonce }) {
const [data, setData] = useState(cachedData)
useEffect(() => {
fetchData()
.then(data => setData(data))
.then(data => {
cachedData = data
setData(cachedData)
})
}, [])
// TODO: cute spin animation when data loads
return <div style={{ display: "flex", flexDirection: "column", width: "100%" }}>
{data && <DataView data={data} captionPortal={captionPortal}/>}
{data && <DataView data={data} nonce={nonce} captionPortal={captionPortal}/>}
{!data && "Loading..."}
</div>
}
const MONTH = 2678400
const monthDates = []
{
let date = new Date(2020, 3, 1)
while (date < Date.now()) {
monthDates.push(date / 1000)
function DataView({ data, captionPortal }) {
const [chartDimensions, chartRef] = useDimensions()
date = new Date(date) // clone
date.setMonth(date.getMonth() + 1)
}
}
function DataView({ data, captionPortal, nonce }) {
const latest = data[data.length - 1]
const oldest = data[0]
let monthDates = []
for (let i = new Date(1583020800000); i < new Date(latest.timestamp); i.setMonth(i.getMonth() + 1)) {
if (i > new Date(oldest.timestamp)) {
monthDates.push(i)
}
}
console.log(monthDates)
const [selectedEntry, setSelectedEntry] = useState(null)
function renderTooltip(tip) {
@ -80,6 +87,8 @@ function DataView({ data, captionPortal }) {
return <span/>
}
const maxPercent = Math.ceil(latest.percentBytes / 25) * 25
return <>
<table width="250" className="outline-invert">
<tbody>
@ -92,30 +101,32 @@ function DataView({ data, captionPortal }) {
<div className="shadow-box flex-grow">
<div className="shadow-box-inner" style={{ paddingRight: ".7em", paddingTop: ".7em", "--text-outline": "transparent" }}>
<div className="progress-chart" ref={chartRef}>
{chartDimensions.width > 0 && <AreaChart width={chartDimensions.width} height={chartDimensions.height} data={data}>
<XAxis dataKey="timestamp" type="number" scale="time" domain={["dataMin", "dataMax"]} ticks={monthDates} tickFormatter={formatDate}/>
<YAxis type="number" unit="%" domain={[0, 100]} tickCount={11}/>
<div className="progress-chart">
<ResponsiveContainer>
<AreaChart data={data}>
<XAxis dataKey="timestamp" type="number" scale={scale} domain={["dataMin", "dataMax"]} ticks={monthDates} tickFormatter={formatTimestampMonth} interval={0}/>
<YAxis type="number" unit="%" domain={[0, maxPercent]} tickCount={maxPercent / 5 + 1}/>
<CartesianGrid stroke="#eee" horizontalPoints={monthDates}/>
<CartesianGrid stroke="#eee"/>
<Area
type="linear"
dataKey="percentBytes"
unit="%"
stroke="#e3ac34" strokeWidth={2}
fill="#edc97e"
dot={true}
isAnimationActive={false}
/>
<Area
type="linear"
dataKey="percentBytes"
unit="%"
stroke="#e3ac34" strokeWidth={2}
fill="#edc97e"
dot={true}
isAnimationActive={false}
/>
<Tooltip content={renderTooltip}/>
</AreaChart>}
<Tooltip content={renderTooltip}/>
</AreaChart>
</ResponsiveContainer>
</div>
</div>
<button className="shadow-box-title yellow">
{selectedEntry ? formatDate(selectedEntry.timestamp, {
{selectedEntry ? formatTimestamp(selectedEntry.timestamp, {
dateStyle: "long",
timeStyle: "short",
}) : ""}
@ -126,12 +137,25 @@ function DataView({ data, captionPortal }) {
</>
}
function formatDate(timestamp, options={}) {
function formatTimestamp(timestamp, options={}) {
const date = new Date(timestamp * 1000)
return new Intl.DateTimeFormat([], options).format(date)
}
function formatTimestampMonth(timestamp) {
const date = new Date(timestamp * 1000)
const [day, month, year] = new Intl.DateTimeFormat("en-GB", {
dateStyle: "medium",
}).format(date).split(" ")
if (month === "Jan") {
return year
}
return month
}
function EntryInfo({ entry }) {
/*const [commitMessage, setCommitMessage] = useState(null)

View File

@ -9,40 +9,6 @@
</head>
<body>
<div id="container">
<!--
<nav>
<button>Info</button>
<button>Progress</button>
<button>Map</button>
<button>Party</button>
<button>Spirits</button>
<button>Map</button>
</nav>
<main id="main">
<table class="outline-invert" width="400">
<tr>
<td>Overall</td>
<td class="thin align-right" id="matched-rom-percent"></td>
</tr>
<tr>
<td>Functions</td>
<td class="thin align-right" id="functions-ratio"></td>
</tr>
<tr>
<td>Play Time</td>
<td class="thin align-right" id="play-time"></td>
</tr>
</table>
<div class="shadow-box flex-grow" id="progress-chart-container">
<canvas class="shadow-box-inner" id="progress-chart"></canvas>
<div class="shadow-box-title" id="progress-chart-tooltip-title"></div>
</div>
<div id="progress-chart-tooltip-description"></div>
</main>
-->
</div>
<div id="container"></div>
</body>
</html>

View File

@ -27,9 +27,7 @@ function App() {
const [tabIndex, setTabIndex] = useState(routedTabIndex)
const [rotation, setRotation] = useState(0)
const [flip, setFlip] = useState(false)
const pane = useRef()
let lockTabs = false // not state
function switchToTab(index) {
if (index === paneIndex || index === tabIndex) return
@ -65,9 +63,6 @@ function App() {
key={tab.name}
className={clsx("tab", tab.color, { "inactive": index !== tabIndex })}
onClick={() => {
if (lockTabs) return
lockTabs = true
switchToTab(index)
}}
>
@ -84,10 +79,10 @@ function App() {
transform: `rotateX(${flip ? '180deg' : '0deg'})`,
overflow: "hidden",
}}>
{tabs[paneIndex].pane({ captionPortal })}
{tabs[paneIndex].pane({ captionPortal, nonce: rotation })}
</div>
</main>
<div class="caption outline-invert" ref={captionPortal}></div>
<div className="caption outline-invert" ref={captionPortal}></div>
</>
}

View File

@ -1,121 +0,0 @@
import Chart from "chart.js"
const csvVersions = {
"1": {
timestamp: s => new Date(parseInt(s) * 1000),
commit: s => s,
totalFuncs: parseInt,
nonMatchingFuncs: parseInt,
matchingFuncs: parseInt,
totalBytes: parseInt,
nonMatchingBytes: parseInt,
matchingBytes: parseInt,
},
}
export async function fetchData() {
const csv = await fetch("https://papermar.io/reports/progress.csv")
.then(response => response.text())
return csv
.split("\n")
.filter(row => row.length)
.map(row => {
const [version, ...data] = row.split(",")
const structure = csvVersions[version]
const obj = {}
for (const [key, transform] of Object.entries(structure)) {
obj[key] = transform(data.shift())
}
return obj
})
}
export async function functionsChart(data, ctx) {
return new Chart(ctx, {
type: "line",
data: {
datasets: [
{
label: "Matching Functions",
borderColor: "transparent",
backgroundColor: "#7f5617",
pointBackgroundColor: "transparent",
pointBorderColor: "black",
data: data.map(row => {
return {
x: row.timestamp,
y: row.matchingFuncs,
}
}),
lineTension: 0,
},
{
label: "Split Functions",
borderColor: "transparent",
backgroundColor: "#b48b31",
pointBackgroundColor: "transparent",
pointBorderColor: "black",
data: data.map(row => {
return {
x: row.timestamp,
y: row.totalFuncs,
}
}),
lineTension: 0,
},
],
},
options: {
responsive: true,
maintainAspectRatio: false,
scales: {
xAxes: [
{
type: "time",
//distribution: "series",
ticks: {
max: Date.now(),
fontFamily: "Paper Mario Dialog Redesigned",
},
time: {
isoWeekday: true,
unit: "month",
},
gridLines: {
drawBorder: true,
}
},
],
yAxes: [
{
label: "Functions",
ticks: {
fontFamily: "Paper Mario Dialog Redesigned",
}
}
]
},
tooltips: {
enabled: false,
intersect: false,
custom(tooltipModel) {
const title = document.getElementById("progress-chart-tooltip-title")
const desc = document.getElementById("rogress-chart-tooltip-description")
if (!tooltipModel.dataPoints) {
title.innerText = ""
desc.innerText = ""
}
const row = data[tooltipModel.dataPoints[0].index]
console.log(row)
title.innerText = row.commit
},
},
},
})
}