7d8f19e6e4
- 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
119 lines
3.4 KiB
Svelte
119 lines
3.4 KiB
Svelte
<script lang="typescript">
|
|
import { createEventDispatcher } from "svelte";
|
|
import type pb from "anki/backend_proto";
|
|
import type { I18n } from "anki/i18n";
|
|
|
|
import Graph from "./Graph.svelte";
|
|
import InputBox from "./InputBox.svelte";
|
|
|
|
import { defaultGraphBounds } from "./graph-helpers";
|
|
import type { SearchEventMap } from "./graph-helpers";
|
|
import { gatherData, renderCards } from "./card-counts";
|
|
import type { GraphData, TableDatum } from "./card-counts";
|
|
import type { PreferenceStore } from "./preferences";
|
|
|
|
export let sourceData: pb.BackendProto.GraphsOut;
|
|
export let i18n: I18n;
|
|
export let preferences: PreferenceStore;
|
|
|
|
let { cardCountsSeparateInactive, browserLinksSupported } = preferences;
|
|
const dispatch = createEventDispatcher<SearchEventMap>();
|
|
|
|
let svg = null as HTMLElement | SVGElement | null;
|
|
|
|
let bounds = defaultGraphBounds();
|
|
bounds.width = 225;
|
|
bounds.marginBottom = 0;
|
|
|
|
let graphData = (null as unknown) as GraphData;
|
|
let tableData = (null as unknown) as TableDatum[];
|
|
|
|
$: {
|
|
graphData = gatherData(sourceData, $cardCountsSeparateInactive, i18n);
|
|
tableData = renderCards(svg as any, bounds, graphData);
|
|
}
|
|
|
|
const label = i18n.tr(i18n.TR.STATISTICS_COUNTS_SEPARATE_SUSPENDED_BURIED_CARDS);
|
|
const total = i18n.tr(i18n.TR.STATISTICS_COUNTS_TOTAL_CARDS);
|
|
</script>
|
|
|
|
<style lang="scss">
|
|
svg {
|
|
transition: opacity 1s;
|
|
}
|
|
|
|
.counts-outer {
|
|
display: flex;
|
|
justify-content: center;
|
|
}
|
|
|
|
.counts-table {
|
|
display: flex;
|
|
flex-direction: column;
|
|
justify-content: center;
|
|
|
|
td {
|
|
white-space: nowrap;
|
|
}
|
|
}
|
|
|
|
table {
|
|
border-spacing: 1em 0;
|
|
}
|
|
|
|
.right {
|
|
text-align: right;
|
|
}
|
|
|
|
.search-link:hover {
|
|
cursor: pointer;
|
|
color: var(--link);
|
|
text-decoration: underline;
|
|
}
|
|
</style>
|
|
|
|
<Graph title={graphData.title}>
|
|
<InputBox>
|
|
<label>
|
|
<input type="checkbox" bind:checked={$cardCountsSeparateInactive} />
|
|
{label}
|
|
</label>
|
|
</InputBox>
|
|
|
|
<div class="counts-outer">
|
|
<svg
|
|
bind:this={svg}
|
|
viewBox={`0 0 ${bounds.width} ${bounds.height}`}
|
|
width={bounds.width}
|
|
height={bounds.height}
|
|
style="opacity: {graphData.totalCards ? 1 : 0}">
|
|
<g class="counts" />
|
|
</svg>
|
|
<div class="counts-table">
|
|
<table>
|
|
{#each tableData as d, _idx}
|
|
<tr>
|
|
<!-- prettier-ignore -->
|
|
<td>
|
|
<span style="color: {d.colour};">■ </span>
|
|
{#if browserLinksSupported}
|
|
<span class="search-link" on:click={() => dispatch('search', { query: d.query })}>{d.label}</span>
|
|
{:else}
|
|
<span>{d.label}</span>
|
|
{/if}
|
|
</td>
|
|
<td class="right">{d.count}</td>
|
|
<td class="right">{d.percent}</td>
|
|
</tr>
|
|
{/each}
|
|
|
|
<tr>
|
|
<td><span style="visibility: hidden;">■</span> {total}</td>
|
|
<td class="right">{graphData.totalCards}</td>
|
|
<td />
|
|
</tr>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
</Graph>
|