Add help modal to TR table (#3874)

* Update TrueRetention.svelte adding description

* Update statistics.ftl to add additional info

* Swap TR with DR

* Change string to 'Is expected to'

* Add help modal to TR table

* Add tooltip slot to Graph.svelte (thanks @Luc-Mcgrady)

* Fix lint warning and failing test

* Remove unused code

* removedd on:mount to make eslint happy

* ADD back on:mount

* ADD back code needed for on:mount

* REMOVE openHelpModal() as I couldn't figure out how to make the title clickable

* attempt to ADD clickable title (BROKEN\!)

* Update ts/lib/components/TitledContainer.svelte

Co-authored-by: Luc Mcgrady <lucmcgrady@gmail.com>

* Update ts/routes/graphs/Graph.svelte

Co-authored-by: Luc Mcgrady <lucmcgrady@gmail.com>

* Update ts/routes/graphs/TrueRetention.svelte

Co-authored-by: Luc Mcgrady <lucmcgrady@gmail.com>

* ADD exported onTitleClick as @Luc-Mcgrady suggested

* REMOVE vite.config.ts file

---------

Co-authored-by: Luc Mcgrady <lucmcgrady@gmail.com>
This commit is contained in:
GithubAnon0000 2025-04-24 08:31:45 +00:00 committed by GitHub
parent 79b19a17a3
commit aacf8ec774
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 57 additions and 3 deletions

View file

@ -114,6 +114,7 @@ statistics-counts-separate-suspended-buried-cards = Separate suspended/buried ca
statistics-true-retention-title = True Retention statistics-true-retention-title = True Retention
statistics-true-retention-subtitle = Pass rate of cards with an interval ≥ 1 day. statistics-true-retention-subtitle = Pass rate of cards with an interval ≥ 1 day.
statistics-true-retention-tooltip = If you are using FSRS, your true retention is expected to be close to your desired retention. Please keep in mind that data for a single day is noisy, so it's better to look at monthly data.
statistics-true-retention-range = Range statistics-true-retention-range = Range
statistics-true-retention-pass = Pass statistics-true-retention-pass = Pass
statistics-true-retention-fail = Fail statistics-true-retention-fail = Fail

View file

@ -12,6 +12,7 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
export { className as class }; export { className as class };
export let title: string; export let title: string;
export let onTitleClick: ((_e: MouseEvent | KeyboardEvent) => void) | null = null;
</script> </script>
<div <div
@ -24,7 +25,22 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
style:--container-margin="0" style:--container-margin="0"
> >
<div class="position-relative"> <div class="position-relative">
<h1>{title}</h1> {#if onTitleClick}
<span
on:click={onTitleClick}
on:keydown={onTitleClick}
role="button"
tabindex="0"
>
<h1>
{title}
</h1>
</span>
{:else}
<h1>
{title}
</h1>
{/if}
<div class="help-badge position-absolute" class:rtl> <div class="help-badge position-absolute" class:rtl>
<slot name="tooltip" /> <slot name="tooltip" />
</div> </div>

View file

@ -8,6 +8,7 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
// When title is null (default), the graph is inlined, not having TitledContainer wrapper. // When title is null (default), the graph is inlined, not having TitledContainer wrapper.
export let title: string | null = null; export let title: string | null = null;
export let subtitle: string | null = null; export let subtitle: string | null = null;
export let onTitleClick: ((_e: MouseEvent | KeyboardEvent) => void) | null = null;
</script> </script>
{#if title == null} {#if title == null}
@ -18,7 +19,8 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
<slot /> <slot />
</div> </div>
{:else} {:else}
<TitledContainer class="d-flex flex-column" {title}> <TitledContainer class="d-flex flex-column" {title} {onTitleClick}>
<slot slot="tooltip" name="tooltip"></slot>
<div class="graph d-flex flex-grow-1 flex-column justify-content-center"> <div class="graph d-flex flex-grow-1 flex-column justify-content-center">
{#if subtitle} {#if subtitle}
<div class="subtitle">{subtitle}</div> <div class="subtitle">{subtitle}</div>

View file

@ -5,6 +5,11 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
<script lang="ts"> <script lang="ts">
import type { GraphsResponse } from "@generated/anki/stats_pb"; import type { GraphsResponse } from "@generated/anki/stats_pb";
import * as tr from "@generated/ftl"; import * as tr from "@generated/ftl";
import { HelpPage } from "@tslib/help-page";
import HelpModal from "$lib/components/HelpModal.svelte";
import type Carousel from "bootstrap/js/dist/carousel";
import type Modal from "bootstrap/js/dist/modal";
import type { HelpItem } from "$lib/components/types";
import { type RevlogRange } from "./graph-helpers"; import { type RevlogRange } from "./graph-helpers";
import { DisplayMode, type PeriodTrueRetentionData, Scope } from "./true-retention"; import { DisplayMode, type PeriodTrueRetentionData, Scope } from "./true-retention";
@ -31,13 +36,43 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
} }
}); });
const retentionHelp = {
trueRetention: {
title: tr.statisticsTrueRetentionTitle(),
help: tr.statisticsTrueRetentionTooltip(),
},
};
const helpSections: HelpItem[] = Object.values(retentionHelp);
let modal: Modal;
let carousel: Carousel;
function openHelpModal(index: number): void {
modal.show();
carousel.to(index);
}
let mode: DisplayMode = $state(DisplayMode.Summary); let mode: DisplayMode = $state(DisplayMode.Summary);
const title = tr.statisticsTrueRetentionTitle(); const title = tr.statisticsTrueRetentionTitle();
const subtitle = tr.statisticsTrueRetentionSubtitle(); const subtitle = tr.statisticsTrueRetentionSubtitle();
const onTitleClick = () => {
openHelpModal(Object.keys(retentionHelp).indexOf("trueRetention"));
};
</script> </script>
<Graph {title} {subtitle}> <Graph {title} {subtitle} {onTitleClick}>
<HelpModal
title={tr.statisticsTrueRetentionTitle()}
url={HelpPage.DeckOptions.fsrs}
slot="tooltip"
{helpSections}
on:mount={(e) => {
modal = e.detail.modal;
carousel = e.detail.carousel;
}}
/>
<InputBox> <InputBox>
<label> <label>
<input type="radio" bind:group={mode} value={DisplayMode.Young} /> <input type="radio" bind:group={mode} value={DisplayMode.Young} />