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 {