mirror of
https://github.com/pmret/website.git
synced 2024-11-08 12:12:27 +01:00
chart updates
This commit is contained in:
parent
ec241dcd38
commit
a59e59458e
@ -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",
|
||||
|
@ -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)
|
||||
|
||||
|
@ -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>
|
||||
|
@ -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>
|
||||
</>
|
||||
}
|
||||
|
||||
|
121
src/progress.js
121
src/progress.js
@ -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
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
Loading…
Reference in New Issue
Block a user