mirror of
https://github.com/ankitects/anki.git
synced 2025-09-24 16:56:36 -04:00
add remaining tooltip i18n
This commit is contained in:
parent
41b296e96c
commit
68978e7c4e
16 changed files with 146 additions and 113 deletions
|
@ -95,9 +95,19 @@ statistics-future-due-title = Future Due
|
||||||
statistics-reviews-title = Reviews
|
statistics-reviews-title = Reviews
|
||||||
statistics-intervals-title = Review Intervals
|
statistics-intervals-title = Review Intervals
|
||||||
statistics-answer-buttons-title = Answer Buttons
|
statistics-answer-buttons-title = Answer Buttons
|
||||||
|
# eg Button: 4
|
||||||
|
statistics-answer-buttons-button-number = Button
|
||||||
|
# eg Times pressed: 123
|
||||||
|
statistics-answer-buttons-button-pressed = Times pressed
|
||||||
statistics-hours-title = Hourly Breakdown
|
statistics-hours-title = Hourly Breakdown
|
||||||
statistics-added-title = Added
|
statistics-added-title = Added
|
||||||
statistics-card-ease-title = Card Ease
|
statistics-card-ease-title = Card Ease
|
||||||
|
statistics-card-ease-subtitle = The lower the ease, the more frequently a card will appear.
|
||||||
|
# eg "3 cards with 150-170% ease"
|
||||||
|
statistics-card-ease-tooltip = { $cards ->
|
||||||
|
[one] 1 card with { $percent } ease
|
||||||
|
*[other] { $cards } cards with { $percent } ease
|
||||||
|
}
|
||||||
statistics-future-due-subtitle = The number of reviews due in the future.
|
statistics-future-due-subtitle = The number of reviews due in the future.
|
||||||
statistics-added-subtitle = The number of new cards you have added.
|
statistics-added-subtitle = The number of new cards you have added.
|
||||||
statistics-reviews-count-subtitle = The number of questions you have answered.
|
statistics-reviews-count-subtitle = The number of questions you have answered.
|
||||||
|
@ -111,15 +121,26 @@ statistics-in-days-single = { $days ->
|
||||||
[1] Tomorrow
|
[1] Tomorrow
|
||||||
*[other] In { $days } days
|
*[other] In { $days } days
|
||||||
}
|
}
|
||||||
statistics-in-days-range = In { $daysStart }~{ $daysEnd } days
|
statistics-in-days-range = In { $daysStart }-{ $daysEnd } days
|
||||||
statistics-days-ago-single = { $days ->
|
statistics-days-ago-single = { $days ->
|
||||||
[1] Yesterday
|
[1] Yesterday
|
||||||
*[other] { $days } days ago
|
*[other] { $days } days ago
|
||||||
}
|
}
|
||||||
statistics-days-ago-range = { $daysStart }~{ $daysEnd } days ago
|
statistics-days-ago-range = { $daysStart }-{ $daysEnd } days ago
|
||||||
statistics-running-total = Running total
|
statistics-running-total = Running total
|
||||||
statistics-cards-due = { $cards ->
|
statistics-cards-due = { $cards ->
|
||||||
[one] 1 card due
|
[one] 1 card due
|
||||||
*[other] { $cards } cards due
|
*[other] { $cards } cards due
|
||||||
}
|
}
|
||||||
statistics-backlog-checkbox = Backlog
|
statistics-backlog-checkbox = Backlog
|
||||||
|
statistics-intervals-day-range = { $cards ->
|
||||||
|
[one] 1 card with a { $daysStart }~{ $daysEnd } day interval
|
||||||
|
*[other] { $cards } cards with a { $daysStart }~{ $daysEnd } day interval
|
||||||
|
}
|
||||||
|
statistics-intervals-day-single = { $cards ->
|
||||||
|
[one] 1 card with a { $day } day interval
|
||||||
|
*[other] { $cards } card with a { $day } day interval
|
||||||
|
}
|
||||||
|
# hour range, eg "From 14:00-15:00"
|
||||||
|
statistics-hours-range = From { $hourStart }:00~{ $hourEnd }:00
|
||||||
|
statistics-hours-correct = { $correct }/{ $total } correct ({ $percent }%)
|
||||||
|
|
|
@ -19,7 +19,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
$: if (addedData) {
|
$: if (addedData) {
|
||||||
histogramData = buildHistogram(addedData, range);
|
histogramData = buildHistogram(addedData, range, i18n);
|
||||||
}
|
}
|
||||||
|
|
||||||
const title = i18n.tr(i18n.TR.STATISTICS_ADDED_TITLE);
|
const title = i18n.tr(i18n.TR.STATISTICS_ADDED_TITLE);
|
||||||
|
|
|
@ -1,26 +0,0 @@
|
||||||
<script lang="typescript">
|
|
||||||
import { GraphBounds } from "./graphs";
|
|
||||||
import { I18n } from "../i18n";
|
|
||||||
|
|
||||||
export let bounds: GraphBounds;
|
|
||||||
export let xText: string;
|
|
||||||
export let yText: string;
|
|
||||||
export let i18n: I18n;
|
|
||||||
|
|
||||||
let yRotate = 180;
|
|
||||||
if (i18n.supportsVerticalText()) {
|
|
||||||
yRotate = 0;
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<text
|
|
||||||
class="axis-label"
|
|
||||||
transform={`translate(${bounds.width / 2}, ${bounds.height - 5})`}>
|
|
||||||
{xText}
|
|
||||||
</text>
|
|
||||||
<text
|
|
||||||
class="axis-label y-axis-label"
|
|
||||||
transform={`translate(${bounds.marginLeft / 3}, ${(bounds.height - bounds.marginBottom) / 2 + bounds.marginTop})
|
|
||||||
rotate(${yRotate} 0 0)`}>
|
|
||||||
{yText}
|
|
||||||
</text>
|
|
|
@ -1,7 +1,6 @@
|
||||||
<script lang="typescript">
|
<script lang="typescript">
|
||||||
import { defaultGraphBounds } from "./graphs";
|
import { defaultGraphBounds } from "./graphs";
|
||||||
import AxisTicks from "./AxisTicks.svelte";
|
import AxisTicks from "./AxisTicks.svelte";
|
||||||
import AxisLabels from "./AxisLabels.svelte";
|
|
||||||
import { gatherData, GraphData, renderButtons } from "./buttons";
|
import { gatherData, GraphData, renderButtons } from "./buttons";
|
||||||
import pb from "../backend/proto";
|
import pb from "../backend/proto";
|
||||||
import { I18n } from "../i18n";
|
import { I18n } from "../i18n";
|
||||||
|
@ -14,7 +13,7 @@
|
||||||
let svg = null as HTMLElement | SVGElement | null;
|
let svg = null as HTMLElement | SVGElement | null;
|
||||||
|
|
||||||
$: if (sourceData) {
|
$: if (sourceData) {
|
||||||
renderButtons(svg as SVGElement, bounds, gatherData(sourceData));
|
renderButtons(svg as SVGElement, bounds, gatherData(sourceData), i18n);
|
||||||
}
|
}
|
||||||
|
|
||||||
const title = i18n.tr(i18n.TR.STATISTICS_ANSWER_BUTTONS_TITLE);
|
const title = i18n.tr(i18n.TR.STATISTICS_ANSWER_BUTTONS_TITLE);
|
||||||
|
|
|
@ -12,11 +12,11 @@
|
||||||
let histogramData = null as HistogramData | null;
|
let histogramData = null as HistogramData | null;
|
||||||
|
|
||||||
$: if (sourceData) {
|
$: if (sourceData) {
|
||||||
histogramData = prepareData(gatherData(sourceData));
|
histogramData = prepareData(gatherData(sourceData), i18n);
|
||||||
}
|
}
|
||||||
|
|
||||||
const title = i18n.tr(i18n.TR.STATISTICS_CARD_EASE_TITLE);
|
const title = i18n.tr(i18n.TR.STATISTICS_CARD_EASE_TITLE);
|
||||||
const subtitle = "temp"; //i18n.tr(i18n.TR.STATISTICS_EASE_SUBTITLE);
|
const subtitle = i18n.tr(i18n.TR.STATISTICS_CARD_EASE_SUBTITLE);
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
{#if histogramData}
|
{#if histogramData}
|
||||||
|
|
|
@ -140,6 +140,6 @@
|
||||||
<ReviewsGraph {sourceData} {revlogRange} {i18n} />
|
<ReviewsGraph {sourceData} {revlogRange} {i18n} />
|
||||||
<IntervalsGraph {sourceData} {i18n} />
|
<IntervalsGraph {sourceData} {i18n} />
|
||||||
<EaseGraph {sourceData} {i18n} />
|
<EaseGraph {sourceData} {i18n} />
|
||||||
<ButtonsGraph {sourceData} {i18n} />
|
|
||||||
<HourGraph {sourceData} {i18n} />
|
<HourGraph {sourceData} {i18n} />
|
||||||
|
<ButtonsGraph {sourceData} {i18n} />
|
||||||
<AddedGraph {sourceData} {i18n} />
|
<AddedGraph {sourceData} {i18n} />
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
<script lang="typescript">
|
<script lang="typescript">
|
||||||
import { HistogramData, histogramGraph } from "./histogram-graph";
|
import { HistogramData, histogramGraph } from "./histogram-graph";
|
||||||
import AxisLabels from "./AxisLabels.svelte";
|
|
||||||
import AxisTicks from "./AxisTicks.svelte";
|
import AxisTicks from "./AxisTicks.svelte";
|
||||||
import { defaultGraphBounds } from "./graphs";
|
import { defaultGraphBounds } from "./graphs";
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
<script lang="typescript">
|
<script lang="typescript">
|
||||||
import { defaultGraphBounds } from "./graphs";
|
import { defaultGraphBounds } from "./graphs";
|
||||||
import AxisTicks from "./AxisTicks.svelte";
|
import AxisTicks from "./AxisTicks.svelte";
|
||||||
import AxisLabels from "./AxisLabels.svelte";
|
|
||||||
import { gatherData, GraphData, renderHours } from "./hours";
|
import { gatherData, GraphData, renderHours } from "./hours";
|
||||||
import pb from "../backend/proto";
|
import pb from "../backend/proto";
|
||||||
import { I18n } from "../i18n";
|
import { I18n } from "../i18n";
|
||||||
|
@ -14,7 +13,7 @@
|
||||||
let svg = null as HTMLElement | SVGElement | null;
|
let svg = null as HTMLElement | SVGElement | null;
|
||||||
|
|
||||||
$: if (sourceData) {
|
$: if (sourceData) {
|
||||||
renderHours(svg as SVGElement, bounds, gatherData(sourceData));
|
renderHours(svg as SVGElement, bounds, gatherData(sourceData), i18n);
|
||||||
}
|
}
|
||||||
|
|
||||||
const title = i18n.tr(i18n.TR.STATISTICS_HOURS_TITLE);
|
const title = i18n.tr(i18n.TR.STATISTICS_HOURS_TITLE);
|
||||||
|
|
|
@ -25,7 +25,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
$: if (intervalData) {
|
$: if (intervalData) {
|
||||||
histogramData = prepareIntervalData(intervalData, range);
|
histogramData = prepareIntervalData(intervalData, range, i18n);
|
||||||
}
|
}
|
||||||
|
|
||||||
const title = i18n.tr(i18n.TR.STATISTICS_INTERVALS_TITLE);
|
const title = i18n.tr(i18n.TR.STATISTICS_INTERVALS_TITLE);
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
<script lang="typescript">
|
<script lang="typescript">
|
||||||
import { HistogramData, histogramGraph } from "./histogram-graph";
|
import { HistogramData, histogramGraph } from "./histogram-graph";
|
||||||
import AxisLabels from "./AxisLabels.svelte";
|
|
||||||
import AxisTicks from "./AxisTicks.svelte";
|
import AxisTicks from "./AxisTicks.svelte";
|
||||||
import { defaultGraphBounds, RevlogRange } from "./graphs";
|
import { defaultGraphBounds, RevlogRange } from "./graphs";
|
||||||
import { GraphData, gatherData, renderReviews, ReviewRange } from "./reviews";
|
import { GraphData, gatherData, renderReviews, ReviewRange } from "./reviews";
|
||||||
|
|
|
@ -11,6 +11,8 @@ import { extent, histogram } from "d3-array";
|
||||||
import { scaleLinear, scaleSequential } from "d3-scale";
|
import { scaleLinear, scaleSequential } from "d3-scale";
|
||||||
import { HistogramData } from "./histogram-graph";
|
import { HistogramData } from "./histogram-graph";
|
||||||
import { interpolateBlues } from "d3-scale-chromatic";
|
import { interpolateBlues } from "d3-scale-chromatic";
|
||||||
|
import { I18n } from "../i18n";
|
||||||
|
import { dayLabel } from "../time";
|
||||||
|
|
||||||
export enum AddedRange {
|
export enum AddedRange {
|
||||||
Month = 0,
|
Month = 0,
|
||||||
|
@ -31,22 +33,10 @@ export function gatherData(data: pb.BackendProto.GraphsOut): GraphData {
|
||||||
return { daysAdded };
|
return { daysAdded };
|
||||||
}
|
}
|
||||||
|
|
||||||
function hoverText(
|
|
||||||
data: HistogramData,
|
|
||||||
binIdx: number,
|
|
||||||
cumulative: number,
|
|
||||||
_percent: number
|
|
||||||
): string {
|
|
||||||
const bin = data.bins[binIdx];
|
|
||||||
return (
|
|
||||||
`${bin.length} at ${bin.x1! - 1} days.<br>` +
|
|
||||||
` ${cumulative} cards at or below this point.`
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function buildHistogram(
|
export function buildHistogram(
|
||||||
data: GraphData,
|
data: GraphData,
|
||||||
range: AddedRange
|
range: AddedRange,
|
||||||
|
i18n: I18n
|
||||||
): HistogramData | null {
|
): HistogramData | null {
|
||||||
// get min/max
|
// get min/max
|
||||||
const total = data.daysAdded.length;
|
const total = data.daysAdded.length;
|
||||||
|
@ -81,5 +71,19 @@ export function buildHistogram(
|
||||||
|
|
||||||
const colourScale = scaleSequential(interpolateBlues).domain([xMin!, xMax]);
|
const colourScale = scaleSequential(interpolateBlues).domain([xMin!, xMax]);
|
||||||
|
|
||||||
|
function hoverText(
|
||||||
|
data: HistogramData,
|
||||||
|
binIdx: number,
|
||||||
|
cumulative: number,
|
||||||
|
_percent: number
|
||||||
|
): string {
|
||||||
|
const bin = data.bins[binIdx];
|
||||||
|
const day = dayLabel(i18n, bin.x0!, bin.x1!);
|
||||||
|
const cards = i18n.tr(i18n.TR.STATISTICS_CARDS, { cards: bin.length });
|
||||||
|
const total = i18n.tr(i18n.TR.STATISTICS_RUNNING_TOTAL);
|
||||||
|
const totalCards = i18n.tr(i18n.TR.STATISTICS_CARDS, { cards: cumulative });
|
||||||
|
return `${day}:<br>${cards}<br>${total}: ${totalCards}`;
|
||||||
|
}
|
||||||
|
|
||||||
return { scale, bins, total, hoverText, colourScale, showArea: true };
|
return { scale, bins, total, hoverText, colourScale, showArea: true };
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,6 +14,7 @@ import { scaleLinear, scaleBand, scaleSequential } from "d3-scale";
|
||||||
import { axisBottom, axisLeft } from "d3-axis";
|
import { axisBottom, axisLeft } from "d3-axis";
|
||||||
import { showTooltip, hideTooltip } from "./tooltip";
|
import { showTooltip, hideTooltip } from "./tooltip";
|
||||||
import { GraphBounds } from "./graphs";
|
import { GraphBounds } from "./graphs";
|
||||||
|
import { I18n } from "../i18n";
|
||||||
|
|
||||||
type ButtonCounts = [number, number, number, number];
|
type ButtonCounts = [number, number, number, number];
|
||||||
|
|
||||||
|
@ -67,14 +68,11 @@ interface Datum {
|
||||||
count: number;
|
count: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
function tooltipText(d: Datum): string {
|
|
||||||
return JSON.stringify(d);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function renderButtons(
|
export function renderButtons(
|
||||||
svgElem: SVGElement,
|
svgElem: SVGElement,
|
||||||
bounds: GraphBounds,
|
bounds: GraphBounds,
|
||||||
sourceData: GraphData
|
sourceData: GraphData,
|
||||||
|
i18n: I18n
|
||||||
): void {
|
): void {
|
||||||
const data = [
|
const data = [
|
||||||
...sourceData.learning.map((count: number, idx: number) => {
|
...sourceData.learning.map((count: number, idx: number) => {
|
||||||
|
@ -108,9 +106,22 @@ export function renderButtons(
|
||||||
const xGroup = scaleBand()
|
const xGroup = scaleBand()
|
||||||
.domain(["learning", "young", "mature"])
|
.domain(["learning", "young", "mature"])
|
||||||
.range([bounds.marginLeft, bounds.width - bounds.marginRight]);
|
.range([bounds.marginLeft, bounds.width - bounds.marginRight]);
|
||||||
svg.select<SVGGElement>(".x-ticks").transition(trans).call(
|
svg.select<SVGGElement>(".x-ticks")
|
||||||
|
.transition(trans)
|
||||||
|
.call(
|
||||||
axisBottom(xGroup)
|
axisBottom(xGroup)
|
||||||
// .ticks()
|
.tickFormat(((d: string) => {
|
||||||
|
switch (d) {
|
||||||
|
case "learning":
|
||||||
|
return i18n.tr(i18n.TR.STATISTICS_COUNTS_LEARNING_CARDS);
|
||||||
|
case "young":
|
||||||
|
return i18n.tr(i18n.TR.STATISTICS_COUNTS_YOUNG_CARDS);
|
||||||
|
case "mature":
|
||||||
|
return i18n.tr(i18n.TR.STATISTICS_COUNTS_MATURE_CARDS);
|
||||||
|
default:
|
||||||
|
console.log(d);
|
||||||
|
}
|
||||||
|
}) as any)
|
||||||
.tickSizeOuter(0)
|
.tickSizeOuter(0)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -177,6 +188,13 @@ export function renderButtons(
|
||||||
);
|
);
|
||||||
|
|
||||||
// hover/tooltip
|
// hover/tooltip
|
||||||
|
|
||||||
|
function tooltipText(d: Datum): string {
|
||||||
|
const button = i18n.tr(i18n.TR.STATISTICS_ANSWER_BUTTONS_BUTTON_NUMBER);
|
||||||
|
const timesPressed = i18n.tr(i18n.TR.STATISTICS_ANSWER_BUTTONS_BUTTON_PRESSED);
|
||||||
|
return `${button}: ${d.buttonNum}<br>${timesPressed}: ${d.count}`;
|
||||||
|
}
|
||||||
|
|
||||||
svg.select("g.hoverzone")
|
svg.select("g.hoverzone")
|
||||||
.selectAll("rect")
|
.selectAll("rect")
|
||||||
.data(data)
|
.data(data)
|
||||||
|
|
|
@ -12,6 +12,7 @@ import { scaleLinear, scaleSequential } from "d3-scale";
|
||||||
import { CardQueue } from "../cards";
|
import { CardQueue } from "../cards";
|
||||||
import { HistogramData } from "./histogram-graph";
|
import { HistogramData } from "./histogram-graph";
|
||||||
import { interpolateRdYlGn } from "d3-scale-chromatic";
|
import { interpolateRdYlGn } from "d3-scale-chromatic";
|
||||||
|
import { I18n } from "../i18n";
|
||||||
|
|
||||||
export interface GraphData {
|
export interface GraphData {
|
||||||
eases: number[];
|
eases: number[];
|
||||||
|
@ -24,16 +25,7 @@ export function gatherData(data: pb.BackendProto.GraphsOut): GraphData {
|
||||||
return { eases };
|
return { eases };
|
||||||
}
|
}
|
||||||
|
|
||||||
function hoverText(data: HistogramData, binIdx: number, _percent: number): string {
|
export function prepareData(data: GraphData, i18n: I18n): HistogramData | null {
|
||||||
const bin = data.bins[binIdx];
|
|
||||||
const minPct = Math.floor(bin.x0!);
|
|
||||||
const maxPct = Math.floor(bin.x1!);
|
|
||||||
const ease = maxPct - minPct <= 10 ? `${bin.x0}%` : `${bin.x0}%~${bin.x1}%`;
|
|
||||||
|
|
||||||
return `${bin.length} cards with ${ease} ease.`;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function prepareData(data: GraphData): HistogramData | null {
|
|
||||||
// get min/max
|
// get min/max
|
||||||
const allEases = data.eases;
|
const allEases = data.eases;
|
||||||
if (!allEases.length) {
|
if (!allEases.length) {
|
||||||
|
@ -54,5 +46,16 @@ export function prepareData(data: GraphData): HistogramData | null {
|
||||||
|
|
||||||
const colourScale = scaleSequential(interpolateRdYlGn).domain([xMin, 300]);
|
const colourScale = scaleSequential(interpolateRdYlGn).domain([xMin, 300]);
|
||||||
|
|
||||||
|
function hoverText(data: HistogramData, binIdx: number, _percent: number): string {
|
||||||
|
const bin = data.bins[binIdx];
|
||||||
|
const minPct = Math.floor(bin.x0!);
|
||||||
|
const maxPct = Math.floor(bin.x1!);
|
||||||
|
const percent = maxPct - minPct <= 10 ? `${bin.x0}%` : `${bin.x0}%-${bin.x1}%`;
|
||||||
|
return i18n.tr(i18n.TR.STATISTICS_CARD_EASE_TOOLTIP, {
|
||||||
|
cards: bin.length,
|
||||||
|
percent,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
return { scale, bins, total, hoverText, colourScale, showArea: false };
|
return { scale, bins, total, hoverText, colourScale, showArea: false };
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,6 +2,10 @@
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
font-family: Arial;
|
||||||
|
}
|
||||||
|
|
||||||
.graph-tooltip {
|
.graph-tooltip {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
background-color: white;
|
background-color: white;
|
||||||
|
@ -23,25 +27,6 @@
|
||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* .cards-graph-grid {
|
|
||||||
display: flex;
|
|
||||||
flex-wrap: wrap;
|
|
||||||
padding-left: 0;
|
|
||||||
padding-right: 1em;
|
|
||||||
align-items: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.cards-graph-grid svg {
|
|
||||||
flex: 3 0 0;
|
|
||||||
flex-direction: column;
|
|
||||||
min-width: 500px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.cards-graph-grid div.graph-description {
|
|
||||||
flex: 0 0 10em;
|
|
||||||
font-family: "Arial";
|
|
||||||
} */
|
|
||||||
|
|
||||||
.no-domain-line .domain {
|
.no-domain-line .domain {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,6 +15,7 @@ import { axisBottom, axisLeft } from "d3-axis";
|
||||||
import { showTooltip, hideTooltip } from "./tooltip";
|
import { showTooltip, hideTooltip } from "./tooltip";
|
||||||
import { GraphBounds } from "./graphs";
|
import { GraphBounds } from "./graphs";
|
||||||
import { area, curveBasis } from "d3-shape";
|
import { area, curveBasis } from "d3-shape";
|
||||||
|
import { I18n } from "../i18n";
|
||||||
|
|
||||||
type ButtonCounts = [number, number, number, number];
|
type ButtonCounts = [number, number, number, number];
|
||||||
|
|
||||||
|
@ -52,14 +53,11 @@ export function gatherData(data: pb.BackendProto.GraphsOut): GraphData {
|
||||||
return { hours };
|
return { hours };
|
||||||
}
|
}
|
||||||
|
|
||||||
function tooltipText(d: Hour): string {
|
|
||||||
return JSON.stringify(d);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function renderHours(
|
export function renderHours(
|
||||||
svgElem: SVGElement,
|
svgElem: SVGElement,
|
||||||
bounds: GraphBounds,
|
bounds: GraphBounds,
|
||||||
sourceData: GraphData
|
sourceData: GraphData,
|
||||||
|
i18n: I18n
|
||||||
): void {
|
): void {
|
||||||
const data = sourceData.hours;
|
const data = sourceData.hours;
|
||||||
|
|
||||||
|
@ -141,6 +139,19 @@ export function renderHours(
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
|
function tooltipText(d: Hour): string {
|
||||||
|
const hour = i18n.tr(i18n.TR.STATISTICS_HOURS_RANGE, {
|
||||||
|
hourStart: d.hour,
|
||||||
|
hourEnd: d.hour + 1,
|
||||||
|
});
|
||||||
|
const correct = i18n.tr(i18n.TR.STATISTICS_HOURS_CORRECT, {
|
||||||
|
correct: d.correctCount,
|
||||||
|
total: d.totalCount,
|
||||||
|
percent: d.totalCount ? (d.correctCount / d.totalCount) * 100 : 0,
|
||||||
|
});
|
||||||
|
return `${hour}<br>${correct}`;
|
||||||
|
}
|
||||||
|
|
||||||
// hover/tooltip
|
// hover/tooltip
|
||||||
svg.select("g.hoverzone")
|
svg.select("g.hoverzone")
|
||||||
.selectAll("rect")
|
.selectAll("rect")
|
||||||
|
|
|
@ -12,6 +12,7 @@ import { scaleLinear, scaleSequential } from "d3-scale";
|
||||||
import { CardQueue } from "../cards";
|
import { CardQueue } from "../cards";
|
||||||
import { HistogramData } from "./histogram-graph";
|
import { HistogramData } from "./histogram-graph";
|
||||||
import { interpolateBlues } from "d3-scale-chromatic";
|
import { interpolateBlues } from "d3-scale-chromatic";
|
||||||
|
import { I18n } from "../i18n";
|
||||||
|
|
||||||
export interface IntervalGraphData {
|
export interface IntervalGraphData {
|
||||||
intervals: number[];
|
intervals: number[];
|
||||||
|
@ -32,26 +33,32 @@ export function gatherIntervalData(data: pb.BackendProto.GraphsOut): IntervalGra
|
||||||
return { intervals };
|
return { intervals };
|
||||||
}
|
}
|
||||||
|
|
||||||
function hoverText(
|
export function intervalLabel(
|
||||||
data: HistogramData,
|
i18n: I18n,
|
||||||
binIdx: number,
|
daysStart: number,
|
||||||
_cumulative: number,
|
daysEnd: number,
|
||||||
percent: number
|
cards: number
|
||||||
): string {
|
): string {
|
||||||
const bin = data.bins[binIdx];
|
if (daysEnd - daysStart <= 1) {
|
||||||
const interval =
|
// singular
|
||||||
bin.x1! - bin.x0! === 1
|
return i18n.tr(i18n.TR.STATISTICS_INTERVALS_DAY_SINGLE, {
|
||||||
? `${bin.x0} day interval`
|
day: daysStart,
|
||||||
: `${bin.x0}~${bin.x1} day interval`;
|
cards,
|
||||||
return (
|
});
|
||||||
`${bin.length} cards with ${interval}. ` +
|
} else {
|
||||||
`<br>${percent.toFixed(1)}% cards at or before this point.`
|
// range
|
||||||
);
|
return i18n.tr(i18n.TR.STATISTICS_INTERVALS_DAY_RANGE, {
|
||||||
|
daysStart,
|
||||||
|
daysEnd,
|
||||||
|
cards,
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function prepareIntervalData(
|
export function prepareIntervalData(
|
||||||
data: IntervalGraphData,
|
data: IntervalGraphData,
|
||||||
range: IntervalRange
|
range: IntervalRange,
|
||||||
|
i18n: I18n
|
||||||
): HistogramData | null {
|
): HistogramData | null {
|
||||||
// get min/max
|
// get min/max
|
||||||
const allIntervals = data.intervals;
|
const allIntervals = data.intervals;
|
||||||
|
@ -60,7 +67,7 @@ export function prepareIntervalData(
|
||||||
}
|
}
|
||||||
|
|
||||||
const total = allIntervals.length;
|
const total = allIntervals.length;
|
||||||
const [xMin, origXMax] = extent(allIntervals);
|
const [_xMinOrig, origXMax] = extent(allIntervals);
|
||||||
let xMax = origXMax;
|
let xMax = origXMax;
|
||||||
|
|
||||||
// cap max to selected range
|
// cap max to selected range
|
||||||
|
@ -80,6 +87,7 @@ export function prepareIntervalData(
|
||||||
case IntervalRange.All:
|
case IntervalRange.All:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
const xMin = 0;
|
||||||
xMax = xMax! + 1;
|
xMax = xMax! + 1;
|
||||||
|
|
||||||
// cap bars to available range
|
// cap bars to available range
|
||||||
|
@ -94,5 +102,18 @@ export function prepareIntervalData(
|
||||||
const shiftedMin = xMin! - Math.round((xMax - xMin!) / 10);
|
const shiftedMin = xMin! - Math.round((xMax - xMin!) / 10);
|
||||||
const colourScale = scaleSequential(interpolateBlues).domain([shiftedMin, xMax]);
|
const colourScale = scaleSequential(interpolateBlues).domain([shiftedMin, xMax]);
|
||||||
|
|
||||||
|
function hoverText(
|
||||||
|
data: HistogramData,
|
||||||
|
binIdx: number,
|
||||||
|
_cumulative: number,
|
||||||
|
percent: number
|
||||||
|
): string {
|
||||||
|
const bin = data.bins[binIdx];
|
||||||
|
// const day = dayLabel(i18n, bin.x0!, bin.x1!);
|
||||||
|
const interval = intervalLabel(i18n, bin.x0!, bin.x1!, bin.length);
|
||||||
|
const total = i18n.tr(i18n.TR.STATISTICS_RUNNING_TOTAL);
|
||||||
|
return `${interval}<br>${total}: ${percent.toFixed(1)}%`;
|
||||||
|
}
|
||||||
|
|
||||||
return { scale, bins, total, hoverText, colourScale, showArea: true };
|
return { scale, bins, total, hoverText, colourScale, showArea: true };
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue