move card counts tooltip into permanent table
This commit is contained in:
parent
710127d490
commit
1353590a92
@ -1,6 +1,6 @@
|
|||||||
<script lang="typescript">
|
<script lang="typescript">
|
||||||
import { defaultGraphBounds } from "./graphs";
|
import { defaultGraphBounds } from "./graphs";
|
||||||
import { gatherData, GraphData, renderCards } from "./card-counts";
|
import { gatherData, GraphData, renderCards, TableDatum } from "./card-counts";
|
||||||
import pb from "../backend/proto";
|
import pb from "../backend/proto";
|
||||||
import { I18n } from "../i18n";
|
import { I18n } from "../i18n";
|
||||||
|
|
||||||
@ -15,10 +15,16 @@
|
|||||||
bounds.marginRight = 20;
|
bounds.marginRight = 20;
|
||||||
bounds.marginTop = 0;
|
bounds.marginTop = 0;
|
||||||
|
|
||||||
|
let activeIdx: null | number = null;
|
||||||
|
function onHover(idx: null | number): void {
|
||||||
|
activeIdx = idx;
|
||||||
|
}
|
||||||
|
|
||||||
let graphData = (null as unknown) as GraphData;
|
let graphData = (null as unknown) as GraphData;
|
||||||
|
let tableData = (null as unknown) as TableDatum[];
|
||||||
$: {
|
$: {
|
||||||
graphData = gatherData(sourceData, i18n);
|
graphData = gatherData(sourceData, i18n);
|
||||||
renderCards(svg as any, bounds, graphData);
|
tableData = renderCards(svg as any, bounds, graphData, onHover);
|
||||||
}
|
}
|
||||||
|
|
||||||
const total = i18n.tr(i18n.TR.STATISTICS_COUNTS_TOTAL_CARDS);
|
const total = i18n.tr(i18n.TR.STATISTICS_COUNTS_TOTAL_CARDS);
|
||||||
@ -28,6 +34,23 @@
|
|||||||
svg {
|
svg {
|
||||||
transition: opacity 1s;
|
transition: opacity 1s;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.counts-table {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
table {
|
||||||
|
border-spacing: 1em 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.right {
|
||||||
|
text-align: right;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bold {
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
<div class="graph" id="graph-card-counts">
|
<div class="graph" id="graph-card-counts">
|
||||||
@ -40,6 +63,29 @@
|
|||||||
<g class="days" />
|
<g class="days" />
|
||||||
</svg>
|
</svg>
|
||||||
|
|
||||||
<div class="centered">{total}: {graphData.totalCards}</div>
|
<div class="counts-table">
|
||||||
|
<table>
|
||||||
|
{#each tableData as d, idx}
|
||||||
|
<tr class:bold={activeIdx === idx}>
|
||||||
|
<td>
|
||||||
|
<span style="color: {d.colour};">■</span>
|
||||||
|
{d.label}
|
||||||
|
</td>
|
||||||
|
<td class="right">{d.count}</td>
|
||||||
|
<td class="right">{d.percent}</td>
|
||||||
|
</tr>
|
||||||
|
{/each}
|
||||||
|
|
||||||
|
<tr class:bold={activeIdx === null}>
|
||||||
|
<td>
|
||||||
|
<span style="visibility: hidden;">■</span>
|
||||||
|
{total}
|
||||||
|
</td>
|
||||||
|
<td class="right">{graphData.totalCards}</td>
|
||||||
|
<td />
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
@ -10,9 +10,8 @@ import { CardQueue } from "../cards";
|
|||||||
import pb from "../backend/proto";
|
import pb from "../backend/proto";
|
||||||
import { schemeGreens, schemeBlues } from "d3-scale-chromatic";
|
import { schemeGreens, schemeBlues } from "d3-scale-chromatic";
|
||||||
import "d3-transition";
|
import "d3-transition";
|
||||||
import { select, mouse } from "d3-selection";
|
import { select } from "d3-selection";
|
||||||
import { scaleLinear } from "d3-scale";
|
import { scaleLinear } from "d3-scale";
|
||||||
import { showTooltip, hideTooltip } from "./tooltip";
|
|
||||||
import { GraphBounds } from "./graphs";
|
import { GraphBounds } from "./graphs";
|
||||||
import { cumsum } from "d3-array";
|
import { cumsum } from "d3-array";
|
||||||
import { I18n } from "../i18n";
|
import { I18n } from "../i18n";
|
||||||
@ -98,7 +97,7 @@ function barColour(idx: number): string {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
interface SummedDatum {
|
export interface SummedDatum {
|
||||||
label: string;
|
label: string;
|
||||||
// count of this particular item
|
// count of this particular item
|
||||||
count: number;
|
count: number;
|
||||||
@ -107,11 +106,19 @@ interface SummedDatum {
|
|||||||
total: number;
|
total: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface TableDatum {
|
||||||
|
label: string;
|
||||||
|
count: number;
|
||||||
|
percent: string;
|
||||||
|
colour: string;
|
||||||
|
}
|
||||||
|
|
||||||
export function renderCards(
|
export function renderCards(
|
||||||
svgElem: SVGElement,
|
svgElem: SVGElement,
|
||||||
bounds: GraphBounds,
|
bounds: GraphBounds,
|
||||||
sourceData: GraphData
|
sourceData: GraphData,
|
||||||
): void {
|
onHover: (idx: null | number) => void
|
||||||
|
): TableDatum[] {
|
||||||
const summed = cumsum(sourceData.counts, (d) => d[1]);
|
const summed = cumsum(sourceData.counts, (d) => d[1]);
|
||||||
const data = Array.from(summed).map((n, idx) => {
|
const data = Array.from(summed).map((n, idx) => {
|
||||||
const count = sourceData.counts[idx];
|
const count = sourceData.counts[idx];
|
||||||
@ -131,32 +138,20 @@ export function renderCards(
|
|||||||
|
|
||||||
x.range([bounds.marginLeft, bounds.width - bounds.marginRight - bounds.marginLeft]);
|
x.range([bounds.marginLeft, bounds.width - bounds.marginRight - bounds.marginLeft]);
|
||||||
|
|
||||||
const tooltipText = (current: SummedDatum): string => {
|
const tableData = data.map((d, idx) => {
|
||||||
const rows: string[] = [];
|
const percent = ((d.count / xMax) * 100).toFixed(1);
|
||||||
for (const [idx, d] of data.entries()) {
|
return {
|
||||||
const pct = ((d.count / xMax) * 100).toFixed(2);
|
label: d.label,
|
||||||
const colour = `<span style="color: ${barColour(idx)};">■</span>`;
|
count: d.count,
|
||||||
let label = `${colour} ${d.label}`;
|
percent: `${percent}%`,
|
||||||
if (idx === current.idx) {
|
colour: barColour(idx),
|
||||||
label = `<b>${label}</b>`;
|
} as TableDatum;
|
||||||
}
|
});
|
||||||
const count = d.count;
|
|
||||||
const pctStr = `${pct}%`;
|
|
||||||
const row = `<tr>
|
|
||||||
<td>${label}</td>
|
|
||||||
<td align=right>${count}</td>
|
|
||||||
<td align=right>${pctStr}</td>
|
|
||||||
</tr>`;
|
|
||||||
rows.push(row);
|
|
||||||
}
|
|
||||||
return `<table>${rows.join("")}</table>`;
|
|
||||||
};
|
|
||||||
|
|
||||||
const updateBar = (sel: any): any => {
|
const updateBar = (sel: any): any => {
|
||||||
return sel
|
return sel
|
||||||
.on("mousemove", function (this: any, d: SummedDatum) {
|
.on("mousemove", function (this: any, d: SummedDatum) {
|
||||||
const [x, y] = mouse(document.body);
|
onHover(d.idx);
|
||||||
showTooltip(tooltipText(d), x, y);
|
|
||||||
})
|
})
|
||||||
.transition(trans)
|
.transition(trans)
|
||||||
.attr("x", (d: SummedDatum) => x(d.total - d.count))
|
.attr("x", (d: SummedDatum) => x(d.total - d.count))
|
||||||
@ -173,8 +168,10 @@ export function renderCards(
|
|||||||
.attr("height", 10)
|
.attr("height", 10)
|
||||||
.attr("y", bounds.marginTop)
|
.attr("y", bounds.marginTop)
|
||||||
.attr("fill", (d: SummedDatum): any => barColour(d.idx))
|
.attr("fill", (d: SummedDatum): any => barColour(d.idx))
|
||||||
.on("mouseout", hideTooltip)
|
.on("mouseout", () => onHover(null))
|
||||||
.call((d) => updateBar(d)),
|
.call((d) => updateBar(d)),
|
||||||
(update) => update.call((d) => updateBar(d))
|
(update) => update.call((d) => updateBar(d))
|
||||||
);
|
);
|
||||||
|
|
||||||
|
return tableData;
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user