diff --git a/ts/d3_missing.d.ts b/ts/d3_missing.d.ts
index 7d3d27f85..6633d321f 100644
--- a/ts/d3_missing.d.ts
+++ b/ts/d3_missing.d.ts
@@ -1,4 +1,4 @@
import "d3-array";
declare module "d3-array" {
- export function cumsum(arg0: number[]): Float64Array;
+ export function cumsum(arg0: any[], arg1?: (any) => number): Float64Array;
}
diff --git a/ts/src/stats/CardCounts.svelte b/ts/src/stats/CardCounts.svelte
index 50aa8c110..c4fe19ed2 100644
--- a/ts/src/stats/CardCounts.svelte
+++ b/ts/src/stats/CardCounts.svelte
@@ -1,37 +1,41 @@
-
-
-{#if cardCounts}
+{#if graphData}
-
{cardCounts.title}
-
- {#each cardCounts.counts as count}
-
-
- {count[0]}
-
-
{count[1]}
-
- {/each}
-
+
{graphData.title}
+
+
+
+
{total}: {graphData.totalCards}
+
{/if}
diff --git a/ts/src/stats/card-counts.ts b/ts/src/stats/card-counts.ts
index 44bf3e027..1926674dd 100644
--- a/ts/src/stats/card-counts.ts
+++ b/ts/src/stats/card-counts.ts
@@ -1,17 +1,31 @@
// Copyright: Ankitects Pty Ltd and contributors
// License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
-import pb from "../backend/proto";
+/* eslint
+@typescript-eslint/no-non-null-assertion: "off",
+@typescript-eslint/no-explicit-any: "off",
+ */
+
import { CardQueue } from "../cards";
+import pb from "../backend/proto";
+import { schemeGreens, schemeBlues } from "d3-scale-chromatic";
+import "d3-transition";
+import { select, mouse } from "d3-selection";
+import { scaleLinear } from "d3-scale";
+import { showTooltip, hideTooltip } from "./tooltip";
+import { GraphBounds } from "./graphs";
+import { cumsum } from "d3-array";
import { I18n } from "../i18n";
type Count = [string, number];
-export interface CardCounts {
+export interface GraphData {
title: string;
counts: Count[];
+ totalCards: number;
}
-export function gatherData(data: pb.BackendProto.GraphsOut, i18n: I18n): CardCounts {
+export function gatherData(data: pb.BackendProto.GraphsOut, i18n: I18n): GraphData {
+ // fixme: handle preview cards
const totalCards = data.cards.length;
let newCards = 0;
let young = 0;
@@ -45,7 +59,6 @@ export function gatherData(data: pb.BackendProto.GraphsOut, i18n: I18n): CardCou
}
const counts = [
- [i18n.tr(i18n.TR.STATISTICS_COUNTS_TOTAL_CARDS), totalCards] as Count,
[i18n.tr(i18n.TR.STATISTICS_COUNTS_NEW_CARDS), newCards] as Count,
[i18n.tr(i18n.TR.STATISTICS_COUNTS_YOUNG_CARDS), young] as Count,
[i18n.tr(i18n.TR.STATISTICS_COUNTS_MATURE_CARDS), mature] as Count,
@@ -56,5 +69,81 @@ export function gatherData(data: pb.BackendProto.GraphsOut, i18n: I18n): CardCou
return {
title: i18n.tr(i18n.TR.STATISTICS_COUNTS_TITLE),
counts,
+ totalCards,
};
}
+
+interface Reviews {
+ mature: number;
+ young: number;
+ learn: number;
+ relearn: number;
+ early: number;
+}
+
+export function renderCards(
+ svgElem: SVGElement,
+ bounds: GraphBounds,
+ sourceData: GraphData
+): void {
+ const summed = cumsum(sourceData.counts, (d) => d[1]);
+ const data = Array.from(summed).map((n, idx) => {
+ return {
+ count: sourceData.counts[idx],
+ idx,
+ total: n,
+ };
+ });
+ const xMax = summed.slice(-1)[0];
+ const x = scaleLinear().domain([0, xMax]);
+ const svg = select(svgElem);
+ const trans = svg.transition().duration(600) as any;
+
+ x.range([bounds.marginLeft, bounds.width - bounds.marginRight]);
+
+ const tooltipText = (d: any): string => {
+ const pct = ((d.count[1] / xMax) * 100).toFixed(2);
+ return `${d.count[0]}: ${d.count[1]} (${pct}%)`;
+ };
+
+ const updateBar = (sel: any): any => {
+ return sel
+ .on("mousemove", function (this: any, d: any) {
+ const [x, y] = mouse(document.body);
+ showTooltip(tooltipText(d), x, y);
+ })
+ .transition(trans)
+ .attr("width", (d) => x(d.total) - bounds.marginLeft);
+ };
+
+ data.reverse();
+ svg.select("g.days")
+ .selectAll("rect")
+ .data(data)
+ .join(
+ (enter) =>
+ enter
+ .append("rect")
+ .attr("height", 10)
+ .attr("x", x(0))
+ .attr("y", bounds.marginTop)
+ .attr("fill", (d: any): any => {
+ switch (d.idx) {
+ case 0:
+ return schemeBlues[5][2];
+ case 1:
+ return schemeGreens[5][2];
+ case 2:
+ return schemeGreens[5][3];
+ case 3:
+ return "#FFDC41";
+ case 4:
+ return "grey";
+ }
+ })
+ .on("mouseout", hideTooltip)
+
+ .call((d) => updateBar(d)),
+ (update) => update.call((d) => updateBar(d))
+ );
+}
diff --git a/ts/src/stats/graphs.scss b/ts/src/stats/graphs.scss
index 187344f9f..3fad7d98a 100644
--- a/ts/src/stats/graphs.scss
+++ b/ts/src/stats/graphs.scss
@@ -139,3 +139,7 @@ body.night-mode {
opacity: 0.1;
}
}
+
+.centered {
+ text-align: center;
+}