From 7d8f19e6e4ec29569fce8df25bc19dec09fc1707 Mon Sep 17 00:00:00 2001 From: Damien Elmes Date: Sun, 21 Mar 2021 22:47:52 +1000 Subject: [PATCH] merge in Henrik's TS/Svelte refactor with some changes - The previous commits moved the majority of the remaining global css into components; move the remaining @emotion/css references into ticks.scss and the styling of the Graph.svelte. This is not as elegant as the emotion solution, but builds a whole lot faster, and most of our styling can be scoped to a component anyway. - Leave the .html files in ts/ for now. AnkiMobile uses them, and AnkiDroid likely will in the future too. In the long run we'll likely move to loading the JS into an existing page instead of loading a separate page, but at that point we can just exclude the .html file from copy_files_into_group() without affecting other clients. Closes #1074 --- qt/aqt/data/web/pages/BUILD.bazel | 1 + ts/congrats/BUILD.bazel | 10 +- ts/congrats/CongratsPage.svelte | 2 + ts/congrats/congrats.html | 13 ++- ts/congrats/{bootstrap.ts => index.ts} | 5 +- ts/graphs/AddedGraph.svelte | 28 ++--- ts/graphs/AxisTicks.svelte | 17 +-- ts/graphs/BUILD.bazel | 21 ++-- ts/graphs/ButtonsGraph.svelte | 21 ++-- ts/graphs/CalendarGraph.svelte | 18 +-- ts/graphs/CardCounts.svelte | 18 +-- ts/graphs/EaseGraph.svelte | 21 ++-- ts/graphs/FutureDue.svelte | 26 ++--- ts/graphs/Graph.svelte | 44 +++++++ ts/graphs/GraphsPage.svelte | 63 ++++++---- ts/graphs/HistogramGraph.svelte | 9 +- ts/graphs/HourGraph.svelte | 21 ++-- ts/graphs/InputBox.svelte | 19 +++ ts/graphs/IntervalsGraph.svelte | 26 ++--- ts/graphs/NoDataOverlay.svelte | 13 +++ ts/graphs/RangeBox.svelte | 55 ++++++++- ts/graphs/ReviewsGraph.svelte | 26 ++--- ts/graphs/TableData.svelte | 17 ++- ts/graphs/TodayStats.svelte | 21 ++-- ts/graphs/buttons.ts | 18 +-- ts/graphs/calendar.ts | 19 ++- ts/graphs/graph-styles.ts | 13 +++ ts/graphs/graphs_shared.scss | 155 ------------------------- ts/graphs/histogram-graph.ts | 29 ++--- ts/graphs/hours.ts | 36 +++--- ts/graphs/{bootstrap.ts => index.ts} | 13 ++- ts/graphs/reviews.ts | 30 ++--- ts/graphs/ticks.scss | 40 +++++++ ts/sass/core.scss | 1 + 34 files changed, 467 insertions(+), 402 deletions(-) rename ts/congrats/{bootstrap.ts => index.ts} (99%) create mode 100644 ts/graphs/Graph.svelte create mode 100644 ts/graphs/InputBox.svelte create mode 100644 ts/graphs/graph-styles.ts delete mode 100644 ts/graphs/graphs_shared.scss rename ts/graphs/{bootstrap.ts => index.ts} (91%) create mode 100644 ts/graphs/ticks.scss diff --git a/qt/aqt/data/web/pages/BUILD.bazel b/qt/aqt/data/web/pages/BUILD.bazel index 6c43e1e89..0140a7e8f 100644 --- a/qt/aqt/data/web/pages/BUILD.bazel +++ b/qt/aqt/data/web/pages/BUILD.bazel @@ -13,6 +13,7 @@ copy_files_into_group( copy_files_into_group( name = "congrats_page", srcs = [ + "congrats.css", "congrats.html", "congrats.js", ], diff --git a/ts/congrats/BUILD.bazel b/ts/congrats/BUILD.bazel index e6b5f4601..be6bb0092 100644 --- a/ts/congrats/BUILD.bazel +++ b/ts/congrats/BUILD.bazel @@ -10,8 +10,8 @@ svelte( ) ts_library( - name = "bootstrap", - srcs = ["bootstrap.ts"], + name = "index", + srcs = ["index.ts"], deps = [ "CongratsPage", "lib", @@ -38,17 +38,19 @@ esbuild( "--global-name=anki", "--inject:ts/protobuf-shim.js", ], - entry_point = "bootstrap.ts", + entry_point = "index.ts", external = [ "protobufjs/light", ], + output_css = True, visibility = ["//visibility:public"], deps = [ "CongratsPage", - "bootstrap", + "index", "//ts/lib", "//ts/lib:backend_proto", "//ts/lib:fluent_proto", + "//ts/sass:core_css", ], ) diff --git a/ts/congrats/CongratsPage.svelte b/ts/congrats/CongratsPage.svelte index b061de04a..ef413a2bd 100644 --- a/ts/congrats/CongratsPage.svelte +++ b/ts/congrats/CongratsPage.svelte @@ -1,4 +1,6 @@ +
+ + - - - diff --git a/ts/congrats/bootstrap.ts b/ts/congrats/index.ts similarity index 99% rename from ts/congrats/bootstrap.ts rename to ts/congrats/index.ts index 360803ceb..e56123e56 100644 --- a/ts/congrats/bootstrap.ts +++ b/ts/congrats/index.ts @@ -1,11 +1,12 @@ // Copyright: Ankitects Pty Ltd and contributors // License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html -import { setupI18n } from "anki/i18n"; -import CongratsPage from "./CongratsPage.svelte"; import { getCongratsInfo } from "./lib"; +import { setupI18n } from "anki/i18n"; import { checkNightMode } from "anki/nightmode"; +import CongratsPage from "./CongratsPage.svelte"; + export async function congrats(target: HTMLDivElement): Promise { checkNightMode(); const i18n = await setupI18n(); diff --git a/ts/graphs/AddedGraph.svelte b/ts/graphs/AddedGraph.svelte index 2014a4ac1..c9e50a48f 100644 --- a/ts/graphs/AddedGraph.svelte +++ b/ts/graphs/AddedGraph.svelte @@ -1,15 +1,19 @@ -
-

{title}

- -
{subtitle}
- -
+ + -
+ -
+ diff --git a/ts/graphs/AxisTicks.svelte b/ts/graphs/AxisTicks.svelte index c0625e799..6832c39b9 100644 --- a/ts/graphs/AxisTicks.svelte +++ b/ts/graphs/AxisTicks.svelte @@ -1,12 +1,15 @@ - - - + + + + + diff --git a/ts/graphs/BUILD.bazel b/ts/graphs/BUILD.bazel index f33764193..c40854b40 100644 --- a/ts/graphs/BUILD.bazel +++ b/ts/graphs/BUILD.bazel @@ -6,12 +6,8 @@ load("//ts:esbuild.bzl", "esbuild") load("@io_bazel_rules_sass//:defs.bzl", "sass_binary") sass_binary( - name = "graphs_shared", - src = "graphs_shared.scss", - visibility = ["//visibility:public"], - deps = [ - "//ts/sass:core_lib", - ], + name = "ticks", + src = "ticks.scss", ) svelte_files = glob(["*.svelte"]) @@ -24,8 +20,8 @@ compile_svelte( ) ts_library( - name = "bootstrap", - srcs = ["bootstrap.ts"], + name = "index", + srcs = ["index.ts"], deps = [ "GraphsPage", "lib", @@ -39,7 +35,7 @@ ts_library( name = "lib", srcs = glob( ["*.ts"], - exclude = ["bootstrap.ts"], + exclude = ["index.ts"], ), deps = [ "//ts/lib", @@ -62,7 +58,7 @@ esbuild( "--global-name=anki", "--inject:ts/protobuf-shim.js", ], - entry_point = "bootstrap.ts", + entry_point = "index.ts", external = [ "protobufjs/light", ], @@ -72,8 +68,9 @@ esbuild( "//ts/lib", "//ts/lib:backend_proto", "//ts/lib:fluent_proto", - "bootstrap", - "graphs_shared", + ":index", + ":ticks", + "//ts/sass:core_css", ] + svelte_names, ) diff --git a/ts/graphs/ButtonsGraph.svelte b/ts/graphs/ButtonsGraph.svelte index d84194125..bbe2b2874 100644 --- a/ts/graphs/ButtonsGraph.svelte +++ b/ts/graphs/ButtonsGraph.svelte @@ -1,12 +1,15 @@ -
-

{title}

- -
{subtitle}
- -
+ + -
+ @@ -41,4 +40,4 @@ -
+ diff --git a/ts/graphs/CalendarGraph.svelte b/ts/graphs/CalendarGraph.svelte index 699814e83..7d72dd307 100644 --- a/ts/graphs/CalendarGraph.svelte +++ b/ts/graphs/CalendarGraph.svelte @@ -1,14 +1,18 @@ -
-

{title}

- -
+ + -
+ @@ -88,4 +90,4 @@ -
+ diff --git a/ts/graphs/CardCounts.svelte b/ts/graphs/CardCounts.svelte index c7152f6ae..384002e7f 100644 --- a/ts/graphs/CardCounts.svelte +++ b/ts/graphs/CardCounts.svelte @@ -1,12 +1,16 @@ -
-

{title}

- -
{subtitle}
- + -
+ diff --git a/ts/graphs/FutureDue.svelte b/ts/graphs/FutureDue.svelte index 8bd3dac49..42ba0acbd 100644 --- a/ts/graphs/FutureDue.svelte +++ b/ts/graphs/FutureDue.svelte @@ -1,15 +1,19 @@ -
-

{title}

- -
{subtitle}
- -
+ + {#if graphData && graphData.haveBacklog}
+ -
+ diff --git a/ts/graphs/Graph.svelte b/ts/graphs/Graph.svelte new file mode 100644 index 000000000..128b15b02 --- /dev/null +++ b/ts/graphs/Graph.svelte @@ -0,0 +1,44 @@ + + + + + + +
+

{title}

+ + {#if subtitle} +
{subtitle}
+ {/if} + + +
diff --git a/ts/graphs/GraphsPage.svelte b/ts/graphs/GraphsPage.svelte index b7eab4172..2c52979e6 100644 --- a/ts/graphs/GraphsPage.svelte +++ b/ts/graphs/GraphsPage.svelte @@ -1,5 +1,6 @@ -{#if controller} - -{/if} + + +
+ {#if controller} + + {/if} + + {#if sourceData} +
+ {#each graphs as graph} + + {/each} +
+ {/if} +
diff --git a/ts/graphs/HistogramGraph.svelte b/ts/graphs/HistogramGraph.svelte index e8b11894d..88f91fb3a 100644 --- a/ts/graphs/HistogramGraph.svelte +++ b/ts/graphs/HistogramGraph.svelte @@ -1,13 +1,14 @@ -
-

{title}

- -
{subtitle}
- -
+ + -
+ @@ -42,4 +41,4 @@ -
+ diff --git a/ts/graphs/InputBox.svelte b/ts/graphs/InputBox.svelte new file mode 100644 index 000000000..2cea56d41 --- /dev/null +++ b/ts/graphs/InputBox.svelte @@ -0,0 +1,19 @@ + + +
+ +
diff --git a/ts/graphs/IntervalsGraph.svelte b/ts/graphs/IntervalsGraph.svelte index eedcbccb4..525bf812a 100644 --- a/ts/graphs/IntervalsGraph.svelte +++ b/ts/graphs/IntervalsGraph.svelte @@ -1,6 +1,14 @@ -
-

{title}

- -
{subtitle}
- -
+ + -
+ -
+ diff --git a/ts/graphs/NoDataOverlay.svelte b/ts/graphs/NoDataOverlay.svelte index aaf5c51f7..c4a329b69 100644 --- a/ts/graphs/NoDataOverlay.svelte +++ b/ts/graphs/NoDataOverlay.svelte @@ -6,6 +6,19 @@ const noData = i18n.tr(i18n.TR.STATISTICS_NO_DATA); + + {noData} diff --git a/ts/graphs/RangeBox.svelte b/ts/graphs/RangeBox.svelte index 04a2eb600..553cca1c0 100644 --- a/ts/graphs/RangeBox.svelte +++ b/ts/graphs/RangeBox.svelte @@ -1,6 +1,8 @@ + +
-
+
+ -
+ -
+
diff --git a/ts/graphs/ReviewsGraph.svelte b/ts/graphs/ReviewsGraph.svelte index 7e2361dbe..45b4387e9 100644 --- a/ts/graphs/ReviewsGraph.svelte +++ b/ts/graphs/ReviewsGraph.svelte @@ -1,17 +1,21 @@ -
-

{title}

- -
{subtitle}
- -
+ + -
+ {#each [4, 3, 2, 1, 0] as i} @@ -72,4 +72,4 @@ -
+ diff --git a/ts/graphs/TableData.svelte b/ts/graphs/TableData.svelte index d342937dd..59a63a436 100644 --- a/ts/graphs/TableData.svelte +++ b/ts/graphs/TableData.svelte @@ -6,7 +6,22 @@ export let tableData: TableDatum[]; -
+ + +
{#each tableData as { label, value }} diff --git a/ts/graphs/TodayStats.svelte b/ts/graphs/TodayStats.svelte index dc99f3fef..a4e9ad83a 100644 --- a/ts/graphs/TodayStats.svelte +++ b/ts/graphs/TodayStats.svelte @@ -1,9 +1,12 @@ -{#if todayData} -
-

{todayData.title}

+ -
+{#if todayData} + +
{#each todayData.lines as line}
{line}
{/each}
-
+ {/if} diff --git a/ts/graphs/buttons.ts b/ts/graphs/buttons.ts index 07408fdcf..2a59c5c3f 100644 --- a/ts/graphs/buttons.ts +++ b/ts/graphs/buttons.ts @@ -7,6 +7,7 @@ */ import pb from "anki/backend_proto"; +import type { I18n } from "anki/i18n"; import { interpolateRdYlGn, select, @@ -25,7 +26,6 @@ import { GraphRange, millisecondCutoffForRange, } from "./graph-helpers"; -import type { I18n } from "anki/i18n"; type ButtonCounts = [number, number, number, number]; @@ -153,9 +153,8 @@ export function renderButtons( const xGroup = scaleBand() .domain(["learning", "young", "mature"]) .range([bounds.marginLeft, bounds.width - bounds.marginRight]); - svg.select(".x-ticks") - .transition(trans) - .call( + svg.select(".x-ticks").call((selection) => + selection.transition(trans).call( axisBottom(xGroup) .tickFormat(((d: GroupKind) => { let kind: string; @@ -174,7 +173,8 @@ export function renderButtons( return `${kind} \u200e(${totalCorrect(d).percent}%)`; }) as any) .tickSizeOuter(0) - ); + ) + ); const xButton = scaleBand() .domain(["1", "2", "3", "4"]) @@ -189,13 +189,13 @@ export function renderButtons( const y = scaleLinear() .range([bounds.height - bounds.marginBottom, bounds.marginTop]) .domain([0, yMax]); - svg.select(".y-ticks") - .transition(trans) - .call( + svg.select(".y-ticks").call((selection) => + selection.transition(trans).call( axisLeft(y) .ticks(bounds.height / 50) .tickSizeOuter(0) - ); + ) + ); // x bars diff --git a/ts/graphs/calendar.ts b/ts/graphs/calendar.ts index 842aa42c5..c4e9184db 100644 --- a/ts/graphs/calendar.ts +++ b/ts/graphs/calendar.ts @@ -3,9 +3,9 @@ /* eslint @typescript-eslint/no-non-null-assertion: "off", -@typescript-eslint/no-explicit-any: "off", */ +import type { I18n } from "anki/i18n"; import pb from "anki/backend_proto"; import { interpolateBlues, @@ -21,6 +21,7 @@ import { timeSaturday, } from "d3"; import type { CountableTimeInterval } from "d3"; + import { showTooltip, hideTooltip } from "./tooltip"; import { GraphBounds, @@ -28,7 +29,7 @@ import { RevlogRange, SearchDispatch, } from "./graph-helpers"; -import type { I18n } from "anki/i18n"; +import { clickableClass } from "./graph-styles"; export interface GraphData { // indexed by day, where day is relative to today @@ -203,24 +204,22 @@ export function renderCalendar( .data(data) .join("rect") .attr("fill", emptyColour) - .attr("width", (d) => x(d.weekNumber + 1)! - x(d.weekNumber)! - 2) + .attr("width", (d: DayDatum) => x(d.weekNumber + 1)! - x(d.weekNumber)! - 2) .attr("height", height - 2) - .attr("x", (d) => x(d.weekNumber + 1)!) - .attr("y", (d) => bounds.marginTop + d.weekDay * height) + .attr("x", (d: DayDatum) => x(d.weekNumber + 1)!) + .attr("y", (d: DayDatum) => bounds.marginTop + d.weekDay * height) .on("mousemove", (event: MouseEvent, d: DayDatum) => { const [x, y] = pointer(event, document.body); showTooltip(tooltipText(d), x, y); }) .on("mouseout", hideTooltip) - .attr("class", (d: any): string => { - return d.count > 0 ? "clickable" : ""; - }) - .on("click", function (_event: MouseEvent, d: any) { + .attr("class", (d: DayDatum): string => (d.count > 0 ? clickableClass : "")) + .on("click", function (_event: MouseEvent, d: DayDatum) { if (d.count > 0) { dispatch("search", { query: `"prop:rated=${d.day}"` }); } }) .transition() .duration(800) - .attr("fill", (d) => (d.count === 0 ? emptyColour : blues(d.count)!)); + .attr("fill", (d: DayDatum) => (d.count === 0 ? emptyColour : blues(d.count)!)); } diff --git a/ts/graphs/graph-styles.ts b/ts/graphs/graph-styles.ts new file mode 100644 index 000000000..1361d7f67 --- /dev/null +++ b/ts/graphs/graph-styles.ts @@ -0,0 +1,13 @@ +// Copyright: Ankitects Pty Ltd and contributors +// License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html + +// Global css classes used by subcomponents + +// Graph.svelte +export const oddTickClass = "tick-odd"; +export const clickableClass = "graph-element-clickable"; + +// It would be nice to define these in the svelte file that declares them, +// but currently this trips the tooling up: +// https://github.com/sveltejs/svelte/issues/5817 +// export { oddTickClass, clickableClass } from "./Graph.svelte"; diff --git a/ts/graphs/graphs_shared.scss b/ts/graphs/graphs_shared.scss deleted file mode 100644 index 7b4f0b108..000000000 --- a/ts/graphs/graphs_shared.scss +++ /dev/null @@ -1,155 +0,0 @@ -/* Copyright: Ankitects Pty Ltd and contributors - * License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html */ - -@use '../sass/core'; - -* { - overscroll-behavior: none; -} - -.graph { - margin-left: auto; - margin-right: auto; - max-width: 60em; - page-break-inside: avoid; -} - -.graph h1 { - text-align: center; - margin-bottom: 0.25em; - margin-top: 1.5em; -} - -.no-domain-line .domain { - opacity: 0.05; -} - -.tick { - line { - opacity: 0.1; - } - - text { - opacity: 0.5; - font-size: 10px; - } -} - -@media only screen and (max-width: 800px) { - .tick text { - font-size: 13px; - } -} -@media only screen and (max-width: 600px) { - body { - font-size: 12px; - } - - .tick text { - font-size: 16px; - } - - .tick-odd { - display: none; - } -} - -.range-box { - position: fixed; - z-index: 1; - top: 0; - width: 100%; - color: var(--text-fg); - background: var(--window-bg); - padding: 0.5em; -} - -@media print { - .range-box { - position: absolute; - } -} - -.range-box-pad { - height: 2em; -} - -.range-box-inner { - display: flex; - justify-content: center; -} - -@media only screen and (max-device-width: 480px) and (orientation: portrait) { - .range-box-inner { - font-size: smaller; - } -} - -.range-box-inner > * { - padding-left: 0.5em; - padding-right: 0.5em; -} - -@keyframes spin { - 0% { - -webkit-transform: rotate(0deg); - } - 100% { - -webkit-transform: rotate(360deg); - } -} - -.spin { - position: absolute; - animation: spin; - animation-duration: 1s; - animation-iteration-count: infinite; - display: inline-block; - font-size: 2em; - opacity: 0; -} - -.spin.active { - opacity: 0.5; - transition: opacity 1s; -} - -.legend-outer { - text-align: center; -} - -.subtitle { - text-align: center; - margin-bottom: 1em; -} - -.no-data { - text { - text-anchor: middle; - fill: grey; - } - rect { - fill: var(--window-bg); - } -} - -.centered { - display: flex; - justify-content: center; -} - -.align-end { - text-align: end; -} - -.align-start { - text-align: start; -} - -.no-focus-outline:focus { - outline: 0; -} - -.clickable { - cursor: pointer; -} diff --git a/ts/graphs/histogram-graph.ts b/ts/graphs/histogram-graph.ts index ff2dc6bcf..ab81fb2a5 100644 --- a/ts/graphs/histogram-graph.ts +++ b/ts/graphs/histogram-graph.ts @@ -22,6 +22,7 @@ import { import type { ScaleLinear, ScaleSequential, Bin } from "d3"; import { showTooltip, hideTooltip } from "./tooltip"; import { GraphBounds, setDataAvailable } from "./graph-helpers"; +import { clickableClass } from "./graph-styles"; export interface HistogramData { scale: ScaleLinear; @@ -57,14 +58,14 @@ export function histogramGraph( const binValue = data.binValue ?? ((bin: any): number => bin.length as number); const x = data.scale.range([bounds.marginLeft, bounds.width - bounds.marginRight]); - svg.select(".x-ticks") - .transition(trans) - .call( + svg.select(".x-ticks").call((selection) => + selection.transition(trans).call( axisBottom(x) .ticks(7) .tickSizeOuter(0) .tickFormat((data.xTickFormat ?? null) as any) - ); + ) + ); // y scale @@ -73,13 +74,13 @@ export function histogramGraph( .range([bounds.height - bounds.marginBottom, bounds.marginTop]) .domain([0, yMax]) .nice(); - svg.select(".y-ticks") - .transition(trans) - .call( + svg.select(".y-ticks").call((selection) => + selection.transition(trans).call( axisLeft(y) .ticks(bounds.height / 50) .tickSizeOuter(0) - ); + ) + ); // x bars @@ -125,15 +126,15 @@ export function histogramGraph( const yAreaScale = y.copy().domain([0, data.total]).nice(); if (data.showArea && data.bins.length && areaData.slice(-1)[0]) { - svg.select(".y2-ticks") - .transition(trans) - .call( + svg.select(".y2-ticks").call((selection) => + selection.transition(trans).call( axisRight(yAreaScale) .ticks(bounds.height / 50) .tickSizeOuter(0) - ); + ) + ); - svg.select("path.cumulative-overlay") + svg.select("path.area") .datum(areaData as any) .attr( "d", @@ -179,7 +180,7 @@ export function histogramGraph( if (data.onClick) { hoverzone .filter(([bin]) => bin.length > 0) - .attr("class", "clickable") + .attr("class", clickableClass) .on("click", (_event, [bin]) => data.onClick!(bin)); } } diff --git a/ts/graphs/hours.ts b/ts/graphs/hours.ts index c8918f66b..0ecfe6ad8 100644 --- a/ts/graphs/hours.ts +++ b/ts/graphs/hours.ts @@ -6,6 +6,7 @@ @typescript-eslint/no-explicit-any: "off", */ +import type { I18n } from "anki/i18n"; import pb from "anki/backend_proto"; import { interpolateBlues, @@ -20,6 +21,7 @@ import { area, curveBasis, } from "d3"; + import { showTooltip, hideTooltip } from "./tooltip"; import { GraphBounds, @@ -27,9 +29,7 @@ import { GraphRange, millisecondCutoffForRange, } from "./graph-helpers"; -import type { I18n } from "anki/i18n"; - -type ButtonCounts = [number, number, number, number]; +import { oddTickClass } from "./graph-styles"; interface Hour { hour: number; @@ -97,16 +97,12 @@ export function renderHours( .range([bounds.marginLeft, bounds.width - bounds.marginRight]) .paddingInner(0.1); svg.select(".x-ticks") - .transition(trans) - .call(axisBottom(x).tickSizeOuter(0)) + .call((selection) => + selection.transition(trans).call(axisBottom(x).tickSizeOuter(0)) + ) + .selectAll(".tick") .selectAll("text") - .attr("class", (n: any) => { - if (n % 2 != 0) { - return "tick-odd"; - } else { - return ""; - } - }); + .classed(oddTickClass, (d: any): boolean => d % 2 != 0); const cappedRange = scaleLinear().range([0.1, 0.8]); const colour = scaleSequential((n) => interpolateBlues(cappedRange(n)!)).domain([ @@ -120,13 +116,13 @@ export function renderHours( .range([bounds.height - bounds.marginBottom, bounds.marginTop]) .domain([0, yMax]) .nice(); - svg.select(".y-ticks") - .transition(trans) - .call( + svg.select(".y-ticks").call((selection) => + selection.transition(trans).call( axisLeft(y) .ticks(bounds.height / 50) .tickSizeOuter(0) - ); + ) + ); const yArea = y.copy().domain([0, 1]); @@ -162,14 +158,14 @@ export function renderHours( ) ); - svg.select(".y2-ticks") - .transition(trans) - .call( + svg.select(".y2-ticks").call((selection) => + selection.transition(trans).call( axisRight(yArea) .ticks(bounds.height / 50) .tickFormat((n: any) => `${Math.round(n * 100)}%`) .tickSizeOuter(0) - ); + ) + ); svg.select("path.cumulative-overlay") .datum(data) diff --git a/ts/graphs/bootstrap.ts b/ts/graphs/index.ts similarity index 91% rename from ts/graphs/bootstrap.ts rename to ts/graphs/index.ts index 0884b99cc..4e261cfd6 100644 --- a/ts/graphs/bootstrap.ts +++ b/ts/graphs/index.ts @@ -1,16 +1,13 @@ // Copyright: Ankitects Pty Ltd and contributors // License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html -/* eslint -@typescript-eslint/no-explicit-any: "off", -*/ - import type { SvelteComponent } from "svelte/internal"; import { setupI18n } from "anki/i18n"; -import GraphsPage from "./GraphsPage.svelte"; import { checkNightMode } from "anki/nightmode"; +import GraphsPage from "./GraphsPage.svelte"; + export { default as RangeBox } from "./RangeBox.svelte"; export { default as IntervalsGraph } from "./IntervalsGraph.svelte"; @@ -28,7 +25,11 @@ export { RevlogRange } from "./graph-helpers"; export function graphs( target: HTMLDivElement, graphs: SvelteComponent[], - { search = "deck:current", days = 365, controller = null as any } = {} + { + search = "deck:current", + days = 365, + controller = null as SvelteComponent | null, + } = {} ): void { const nightMode = checkNightMode(); diff --git a/ts/graphs/reviews.ts b/ts/graphs/reviews.ts index 353a700d2..c2f94b5fc 100644 --- a/ts/graphs/reviews.ts +++ b/ts/graphs/reviews.ts @@ -7,6 +7,8 @@ */ import pb from "anki/backend_proto"; +import type { I18n } from "anki/i18n"; +import { timeSpan, dayLabel } from "anki/time"; import { interpolateGreens, interpolateReds, @@ -29,11 +31,9 @@ import { } from "d3"; import type { Bin } from "d3"; -import { showTooltip, hideTooltip } from "./tooltip"; -import { GraphBounds, setDataAvailable, GraphRange } from "./graph-helpers"; import type { TableDatum } from "./graph-helpers"; -import { timeSpan, dayLabel } from "anki/time"; -import type { I18n } from "anki/i18n"; +import { GraphBounds, setDataAvailable, GraphRange } from "./graph-helpers"; +import { showTooltip, hideTooltip } from "./tooltip"; interface Reviews { learn: number; @@ -167,9 +167,9 @@ export function renderReviews( } x.range([bounds.marginLeft, bounds.width - bounds.marginRight]); - svg.select(".x-ticks") - .transition(trans) - .call(axisBottom(x).ticks(7).tickSizeOuter(0)); + svg.select(".x-ticks").call((selection) => + selection.transition(trans).call(axisBottom(x).ticks(7).tickSizeOuter(0)) + ); // y scale @@ -190,14 +190,14 @@ export function renderReviews( .range([bounds.height - bounds.marginBottom, bounds.marginTop]) .domain([0, yMax]) .nice(); - svg.select(".y-ticks") - .transition(trans) - .call( + svg.select(".y-ticks").call((selection) => + selection.transition(trans).call( axisLeft(y) .ticks(bounds.height / 50) .tickSizeOuter(0) .tickFormat(yTickFormat as any) - ); + ) + ); // x bars @@ -331,14 +331,14 @@ export function renderReviews( const yAreaScale = y.copy().domain([0, yCumMax]).nice(); if (yCumMax) { - svg.select(".y2-ticks") - .transition(trans) - .call( + svg.select(".y2-ticks").call((selection) => + selection.transition(trans).call( axisRight(yAreaScale) .ticks(bounds.height / 50) .tickFormat(yTickFormat as any) .tickSizeOuter(0) - ); + ) + ); svg.select("path.cumulative-overlay") .datum(areaData as any) diff --git a/ts/graphs/ticks.scss b/ts/graphs/ticks.scss new file mode 100644 index 000000000..aa33ec990 --- /dev/null +++ b/ts/graphs/ticks.scss @@ -0,0 +1,40 @@ +/* Copyright: Ankitects Pty Ltd and contributors + * License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html */ + +// Customizing the standard x and y tick markers and text on the graphs. The `tick` +// class is automatically added by d3. We apply our custom ticks only to ticks +// that are nested under a Graph component. + +.graph { + .tick { + line { + opacity: 0.1; + } + + text { + opacity: 0.5; + font-size: 10px; + } + } + + @media only screen and (max-width: 800px) { + .tick { + text { + font-size: 13px; + } + } + } + + @media only screen and (max-width: 600px) { + .tick { + text { + font-size: 16px; + // on small screens, hide every second row on graphs that have + // marked the ticks as odd + &.tick-odd { + display: none; + } + } + } + } +} diff --git a/ts/sass/core.scss b/ts/sass/core.scss index 47fd71c7c..37d889833 100644 --- a/ts/sass/core.scss +++ b/ts/sass/core.scss @@ -13,6 +13,7 @@ body { background: var(--window-bg); margin: 1em; transition: opacity 0.5s ease-out; + overscroll-behavior: none; } a {