mirror of
https://github.com/ankitects/anki.git
synced 2025-09-18 22:12:21 -04:00
graphs now work in night mode
This commit is contained in:
parent
1b37398503
commit
101deb002b
13 changed files with 88 additions and 20 deletions
|
@ -9,6 +9,7 @@ import time
|
||||||
import aqt
|
import aqt
|
||||||
from anki.lang import _
|
from anki.lang import _
|
||||||
from aqt.qt import *
|
from aqt.qt import *
|
||||||
|
from aqt.theme import theme_manager
|
||||||
from aqt.utils import (
|
from aqt.utils import (
|
||||||
addCloseShortcut,
|
addCloseShortcut,
|
||||||
getSaveFile,
|
getSaveFile,
|
||||||
|
@ -87,4 +88,8 @@ class DeckStats(QDialog):
|
||||||
|
|
||||||
def refresh(self):
|
def refresh(self):
|
||||||
self.form.web.set_open_links_externally(False)
|
self.form.web.set_open_links_externally(False)
|
||||||
self.form.web.load(QUrl(f"{self.mw.serverURL()}_anki/graphs.html"))
|
if theme_manager.night_mode:
|
||||||
|
extra = "#night"
|
||||||
|
else:
|
||||||
|
extra = ""
|
||||||
|
self.form.web.load(QUrl(f"{self.mw.serverURL()}_anki/graphs.html"+extra))
|
||||||
|
|
2
ts/css.d.ts
vendored
2
ts/css.d.ts
vendored
|
@ -1 +1 @@
|
||||||
declare module "*.css";
|
declare module "*.scss";
|
||||||
|
|
|
@ -31,6 +31,7 @@
|
||||||
"prettier": "^2.0.0",
|
"prettier": "^2.0.0",
|
||||||
"prettier-plugin-svelte": "^1.1.0",
|
"prettier-plugin-svelte": "^1.1.0",
|
||||||
"sass": "^1.26.9",
|
"sass": "^1.26.9",
|
||||||
|
"sass-loader": "^8.0.2",
|
||||||
"style-loader": "^1.2.1",
|
"style-loader": "^1.2.1",
|
||||||
"svelte": "^3.23.2",
|
"svelte": "^3.23.2",
|
||||||
"svelte-loader": "^2.13.6",
|
"svelte-loader": "^2.13.6",
|
||||||
|
|
|
@ -8,6 +8,10 @@
|
||||||
<div id="main"></div>
|
<div id="main"></div>
|
||||||
</body>
|
</body>
|
||||||
<script>
|
<script>
|
||||||
anki.graphs(document.getElementById("main"));
|
const nightMode = window.location.hash == "#night";
|
||||||
|
if (nightMode) {
|
||||||
|
document.body.className = "night-mode";
|
||||||
|
}
|
||||||
|
anki.graphs(document.getElementById("main"), nightMode);
|
||||||
</script>
|
</script>
|
||||||
</html>
|
</html>
|
||||||
|
|
|
@ -11,6 +11,7 @@
|
||||||
export let sourceData: pb.BackendProto.GraphsOut | null = null;
|
export let sourceData: pb.BackendProto.GraphsOut | null = null;
|
||||||
export let revlogRange: RevlogRange = RevlogRange.Month;
|
export let revlogRange: RevlogRange = RevlogRange.Month;
|
||||||
export let i18n: I18n;
|
export let i18n: I18n;
|
||||||
|
export let nightMode: boolean;
|
||||||
|
|
||||||
let graphData: GraphData | null = null;
|
let graphData: GraphData | null = null;
|
||||||
|
|
||||||
|
@ -42,7 +43,14 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
$: if (graphData) {
|
$: if (graphData) {
|
||||||
renderCalendar(svg as SVGElement, bounds, graphData, targetYear, i18n);
|
renderCalendar(
|
||||||
|
svg as SVGElement,
|
||||||
|
bounds,
|
||||||
|
graphData,
|
||||||
|
targetYear,
|
||||||
|
i18n,
|
||||||
|
nightMode
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const title = i18n.tr(i18n.TR.STATISTICS_REVIEWS_TITLE);
|
const title = i18n.tr(i18n.TR.STATISTICS_REVIEWS_TITLE);
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
<script context="module">
|
<script context="module">
|
||||||
import style from "./graphs.css";
|
import style from "./graphs.scss";
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<script lang="typescript">
|
<script lang="typescript">
|
||||||
|
@ -20,6 +20,7 @@
|
||||||
import CalendarGraph from "./CalendarGraph.svelte";
|
import CalendarGraph from "./CalendarGraph.svelte";
|
||||||
|
|
||||||
export let i18n: I18n;
|
export let i18n: I18n;
|
||||||
|
export let nightMode: boolean;
|
||||||
|
|
||||||
let sourceData: pb.BackendProto.GraphsOut | null = null;
|
let sourceData: pb.BackendProto.GraphsOut | null = null;
|
||||||
|
|
||||||
|
@ -142,7 +143,7 @@
|
||||||
|
|
||||||
<TodayStats {sourceData} {i18n} />
|
<TodayStats {sourceData} {i18n} />
|
||||||
<CardCounts {sourceData} {i18n} />
|
<CardCounts {sourceData} {i18n} />
|
||||||
<CalendarGraph {sourceData} {revlogRange} {i18n} />
|
<CalendarGraph {sourceData} {revlogRange} {i18n} {nightMode} />
|
||||||
<FutureDue {sourceData} {revlogRange} {i18n} />
|
<FutureDue {sourceData} {revlogRange} {i18n} />
|
||||||
<ReviewsGraph {sourceData} {revlogRange} {i18n} />
|
<ReviewsGraph {sourceData} {revlogRange} {i18n} />
|
||||||
<IntervalsGraph {sourceData} {i18n} />
|
<IntervalsGraph {sourceData} {i18n} />
|
||||||
|
|
|
@ -154,9 +154,9 @@ export function renderButtons(
|
||||||
.attr("opacity", (d: Datum) => {
|
.attr("opacity", (d: Datum) => {
|
||||||
switch (d.group) {
|
switch (d.group) {
|
||||||
case "learning":
|
case "learning":
|
||||||
return 0.3;
|
return 0.6;
|
||||||
case "young":
|
case "young":
|
||||||
return 0.5;
|
return 0.8;
|
||||||
case "mature":
|
case "mature":
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
|
@ -53,7 +53,8 @@ export function renderCalendar(
|
||||||
bounds: GraphBounds,
|
bounds: GraphBounds,
|
||||||
sourceData: GraphData,
|
sourceData: GraphData,
|
||||||
targetYear: number,
|
targetYear: number,
|
||||||
i18n: I18n
|
i18n: I18n,
|
||||||
|
nightMode: boolean
|
||||||
): void {
|
): void {
|
||||||
const svg = select(svgElem);
|
const svg = select(svgElem);
|
||||||
const now = new Date();
|
const now = new Date();
|
||||||
|
@ -101,7 +102,11 @@ export function renderCalendar(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const data = Array.from(dayMap.values());
|
const data = Array.from(dayMap.values());
|
||||||
const blues = scaleSequential(interpolateBlues).domain([0, maxCount]);
|
const cappedRange = scaleLinear().range([0.2, nightMode ? 0.8 : 1]);
|
||||||
|
const blues = scaleSequential((n) => interpolateBlues(cappedRange(n))).domain([
|
||||||
|
0,
|
||||||
|
maxCount,
|
||||||
|
]);
|
||||||
|
|
||||||
function tooltipText(d: DayDatum): string {
|
function tooltipText(d: DayDatum): string {
|
||||||
const date = d.date.toLocaleString(i18n.langs, {
|
const date = d.date.toLocaleString(i18n.langs, {
|
||||||
|
@ -115,6 +120,10 @@ export function renderCalendar(
|
||||||
}
|
}
|
||||||
|
|
||||||
const height = bounds.height / 10;
|
const height = bounds.height / 10;
|
||||||
|
let emptyColour = "#eee";
|
||||||
|
if (nightMode) {
|
||||||
|
emptyColour = "#333";
|
||||||
|
}
|
||||||
svg.select(`g.days`)
|
svg.select(`g.days`)
|
||||||
.selectAll("rect")
|
.selectAll("rect")
|
||||||
.data(data)
|
.data(data)
|
||||||
|
@ -132,7 +141,7 @@ export function renderCalendar(
|
||||||
.on("mouseout", hideTooltip)
|
.on("mouseout", hideTooltip)
|
||||||
.attr("fill", (d) => {
|
.attr("fill", (d) => {
|
||||||
if (d.count === 0) {
|
if (d.count === 0) {
|
||||||
return "#eee";
|
return emptyColour;
|
||||||
} else {
|
} else {
|
||||||
return blues(d.count);
|
return blues(d.count);
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,11 +4,11 @@
|
||||||
import { setupI18n } from "../i18n";
|
import { setupI18n } from "../i18n";
|
||||||
import GraphsPage from "./GraphsPage.svelte";
|
import GraphsPage from "./GraphsPage.svelte";
|
||||||
|
|
||||||
export function graphs(target: HTMLDivElement): void {
|
export function graphs(target: HTMLDivElement, nightMode: boolean): void {
|
||||||
setupI18n().then((i18n) => {
|
setupI18n().then((i18n) => {
|
||||||
new GraphsPage({
|
new GraphsPage({
|
||||||
target,
|
target,
|
||||||
props: { i18n },
|
props: { i18n, nightMode },
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,9 +1,16 @@
|
||||||
|
$day-fg: black;
|
||||||
|
$day-bg: white;
|
||||||
|
$night-fg: white;
|
||||||
|
$night-bg: #222;
|
||||||
|
|
||||||
* {
|
* {
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
}
|
}
|
||||||
|
|
||||||
body {
|
body {
|
||||||
font-family: Arial;
|
font-family: Arial;
|
||||||
|
background: $day-bg;
|
||||||
|
color: $day-fg;
|
||||||
}
|
}
|
||||||
|
|
||||||
.graph-tooltip {
|
.graph-tooltip {
|
||||||
|
@ -15,7 +22,8 @@ body {
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
transition: opacity 0.3s;
|
transition: opacity 0.3s;
|
||||||
background: #fff;
|
color: $day-fg;
|
||||||
|
background: $day-bg;
|
||||||
}
|
}
|
||||||
|
|
||||||
.graph {
|
.graph {
|
||||||
|
@ -37,7 +45,8 @@ body {
|
||||||
top: 0;
|
top: 0;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 4em;
|
height: 4em;
|
||||||
background: white;
|
color: $day-fg;
|
||||||
|
background: $day-bg;
|
||||||
padding: 0.5em;
|
padding: 0.5em;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -57,7 +66,7 @@ body {
|
||||||
.graph .area {
|
.graph .area {
|
||||||
opacity: 0.05;
|
opacity: 0.05;
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
fill: black;
|
fill: $day-fg;
|
||||||
}
|
}
|
||||||
|
|
||||||
.axis-label {
|
.axis-label {
|
||||||
|
@ -110,3 +119,23 @@ body {
|
||||||
.subtitle {
|
.subtitle {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
body.night-mode {
|
||||||
|
background: $night-bg;
|
||||||
|
color: $night-fg;
|
||||||
|
}
|
||||||
|
|
||||||
|
.night-mode {
|
||||||
|
.graph-tooltip {
|
||||||
|
background: $night-bg;
|
||||||
|
color: $night-fg;
|
||||||
|
}
|
||||||
|
.range-box {
|
||||||
|
background: $night-bg;
|
||||||
|
color: $night-fg;
|
||||||
|
}
|
||||||
|
.graph .area {
|
||||||
|
fill: $night-fg;
|
||||||
|
opacity: 0.1;
|
||||||
|
}
|
||||||
|
}
|
|
@ -40,6 +40,13 @@ export enum RevlogRange {
|
||||||
All = 3,
|
All = 3,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface GraphsContext {
|
||||||
|
cards: pb.BackendProto.Card[];
|
||||||
|
revlog: pb.BackendProto.RevlogEntry[];
|
||||||
|
revlogRange: RevlogRange;
|
||||||
|
nightMode: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
export interface GraphBounds {
|
export interface GraphBounds {
|
||||||
width: number;
|
width: number;
|
||||||
height: number;
|
height: number;
|
||||||
|
|
|
@ -74,7 +74,11 @@ export function renderHours(
|
||||||
.transition(trans)
|
.transition(trans)
|
||||||
.call(axisBottom(x).tickSizeOuter(0));
|
.call(axisBottom(x).tickSizeOuter(0));
|
||||||
|
|
||||||
const colour = scaleSequential(interpolateBlues).domain([0, yMax]);
|
const cappedRange = scaleLinear().range([0.1, 0.8]);
|
||||||
|
const colour = scaleSequential((n) => interpolateBlues(cappedRange(n))).domain([
|
||||||
|
0,
|
||||||
|
yMax,
|
||||||
|
]);
|
||||||
|
|
||||||
// y scale
|
// y scale
|
||||||
|
|
||||||
|
@ -114,7 +118,7 @@ export function renderHours(
|
||||||
.attr("x", (d: Hour) => x(d.hour.toString())!)
|
.attr("x", (d: Hour) => x(d.hour.toString())!)
|
||||||
.attr("y", y(0))
|
.attr("y", y(0))
|
||||||
.attr("height", 0)
|
.attr("height", 0)
|
||||||
.attr("opacity", 0.7)
|
// .attr("opacity", 0.7)
|
||||||
.call(updateBar),
|
.call(updateBar),
|
||||||
(update) => update.call(updateBar),
|
(update) => update.call(updateBar),
|
||||||
(remove) =>
|
(remove) =>
|
||||||
|
|
|
@ -41,8 +41,8 @@ module.exports = {
|
||||||
module: {
|
module: {
|
||||||
rules: [
|
rules: [
|
||||||
{
|
{
|
||||||
test: /\.css$/i,
|
test: /\.s?css$/i,
|
||||||
use: ["style-loader", "css-loader"],
|
use: ["style-loader", "css-loader", "sass-loader"],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
test: /\.(svelte)$/,
|
test: /\.(svelte)$/,
|
||||||
|
|
Loading…
Reference in a new issue