SPEC v0.27: D-128 S-12 line chart of a category or account over the range

New AC-14.11; §13.3 S-12 row with its own range and granularity, the line
chart and the empty-range text; §13.4 strings.csv 569 rows. design-spec
§3.10 SpendLineChart and LineLegend, AnalyticsFilterBar row 2 optional,
S-12 layout for a category or account over the range. strings.csv gains
the seven analytics keys as built in 12e2508. HANDOFF: D-128 and the
keyboard fix landed, web-client programme holds D-129+ and v0.28+.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TQd3pEP9UeN2uifbHknQ7B
This commit is contained in:
2026-10-21 20:34:07 +02:00
parent 12e2508934
commit 44c8cbb771
5 changed files with 46 additions and 15 deletions
+20 -2
View File
@@ -592,7 +592,7 @@ TalkBack: "Konto: Vardag. Dubbeltryck för att byta konto"; "Datum: idag"; "Beta
* `pendingLocal`: hollow dot (2 dp `pending` ring), text in `pending` weight 600 with a 16 dp `cloud_upload` before it: "Din ändring väntar på synk" (AC-12.5); always last.
* Semantics: each item one node; the header is a heading.
### 3.10 Charts (S-11; Vico or the Canvas fallback, §6.2)
### 3.10 Charts (S-11, S-12; Vico or the Canvas fallback, §6.2)
**`ChartCard(title, content)`**: card, padding 18 / 16 / 16, title title-medium; an invisible summary sentence as `contentDescription` of the chart area.
@@ -605,9 +605,19 @@ TalkBack: "Konto: Vardag. Dubbeltryck för att byta konto"; "Datum: idag"; "Beta
**`ChartLegend(groups)`**: wrapping row, gap 4 / 12; 10 dp swatch (radius 3) + label (12 / 16 sp weight 500 `on-surface-variant`). Only groups with data. Decorative for TalkBack (the bars speak their totals).
**`SpendLineChart(points, color, summary, averageOre, movingAverage, xCaption)`** (S-12, AC-14.11, D-128; `chart/LineChart.kt`)
* Plot, y-axis gutter, gridlines, x labels, the "Vecka" caption and horizontal scroll as `StackedBarChart` (the axis code is shared in `Charts.kt`); one point per bucket (every bucket of the range, empty ones as zero, AC-14.7).
* Series: a 2.5 dp line in `color` (category colour or account chart colour) with round joins, 3 dp dots at the points, and a vertical fill under it from 22 % to 2 % opacity of that colour.
* Range average (`averageOre`): a 1.5 dp dashed line (6 dp on, 4 dp off) across the plot at 75 % opacity of `color`, with its amount in whole kronor at the right end in `chartAxis` typography and the same colour.
* Moving average (`movingAverage`, one value per point): a 1.5 dp line in `chart-axis-label` at 85 %.
* A single bucket shows one centred point and no average lines; a range without spending shows only the 0 baseline (D-068).
* Not interactive. TalkBack: the chart is one node reading `summary` (`analytics_summary_line`, or `analytics_summary_line_single` for one bucket).
**`LineLegend(items, color)`**: wrapping row as `ChartLegend`; each item a 16 × 10 dp line swatch in its line's style (solid series in `color`, dashed average, thin neutral moving average) + label (12 / 16 sp weight 500 `on-surface-variant`): the series name, "Snitt" (`analytics_average`), "Glidande snitt (3)" (`analytics_moving_average`). Decorative for TalkBack (the summary names the lines).
**`BreakdownList(rows, total, onRow)`** (AC-14.6 charts 2 and 3): rows sorted by amount descending, padding 8 / 0, gap 6: tile 32 (`CategoryTile` or `AccountTile`) + name (title-small, 1 line, ellipsis; "Arkiverat" badge for archived accounts) + right column `MoneyText(Small)` over the percent (body-small muted, one decimal, half-up, "34,8 %", AC-14.10); below, a 10 dp bar track (`surface-container-highest`, radius full) indented 44 dp, filled to amount / largest amount in the group colour. Row is one node "Boende, 62 000 kronor, 34,8 procent"; click → `onRow(id)`.
**`AnalyticsFilterBar(filter, onChange)`**: row 1: period chip (`date_range`, label "Senaste 6 mån" / "Anpassad · 1–30 nov.", `expand_more`; TalkBack "Period: senaste 6 månaderna") + `ButtonGroup` "Månad | Vecka" (min width 170, fills the rest); row 2: `ButtonGroup` "Hushållet | Min del" and `ButtonGroup` "Kategori | Konto", half width each. Connected buttons: min height 40, label-large weight 600, unselected `surface-container-high` / `on-surface-variant`, selected `secondary` / `on-secondary` with full radius; connected buttons: 8 dp side padding (D-102). 200 %: one control per row. Semantics: each group a radio group named "Indelning", "Mått", "Gruppera efter".
**`AnalyticsFilterBar(filter, onChange)`**: row 1: period chip (`date_range`, label "Senaste 6 mån" / "Anpassad · 1–30 nov.", `expand_more`; TalkBack "Period: senaste 6 månaderna") + `ButtonGroup` "Månad | Vecka" (min width 170, fills the rest); row 2: `ButtonGroup` "Hushållet | Min del" and `ButtonGroup` "Kategori | Konto", half width each; both optional, so S-12 shows row 1 only (D-128). Connected buttons: min height 40, label-large weight 600, unselected `surface-container-high` / `on-surface-variant`, selected `secondary` / `on-secondary` with full radius; connected buttons: 8 dp side padding (D-102). 200 %: one control per row. Semantics: each group a radio group named "Indelning", "Mått", "Gruppera efter".
**`TotalCard(totalOre, caption)`**: card, padding 16 / 20: "Totalt" (label-large muted; "Totalt · Min del" for my share) over `MoneyText(Large)` over the range caption (body-small muted, "maj–okt 2026 · Hushållet").
@@ -971,6 +981,14 @@ Full screen (§13.2), works offline (AC-8.2). The flow and the category rule are
**TalkBack**: header one node "Mat & hushåll, oktober 2026, totalt 3 910,00 kronor, 4 utgifter".
**Category or account over the range (D-128).** Opened from a breakdown row of S-11 (a drill-down from a bar keeps the layout above). Layout:
1. Period chip and "Månad | Vecka" as row 1 of `AnalyticsFilterBar` (no measure or group-by; custom range on its own row; 200 %: one control per row). Both start from S-11's values; changing them recomputes the chart, the total and the list on S-12 only, and S-11 keeps its own. Measure and group-by come from S-11 and cannot be changed here.
2. Header card as above.
3. `ChartCard` "Utgifter över tid" with `SpendLineChart` (§3.10): the series is a 2.5 dp line in the group colour (category colour, or account chart colour) with round joins and 3 dp dots, and a fill from 22 % to 2 % opacity of that colour; the plot, y gutter, gridlines, x labels and scroll are as `StackedBarChart`. The range average (total ÷ buckets, rounded half-up to öre) is a 1.5 dp dashed line (6 on, 4 off) at 75 % opacity of the group colour, its whole-kronor amount at the right end in `chartAxis` and the group colour. The moving average (trailing, 3 buckets: the bucket and the two before it; the first buckets use the 1 or 2 that exist) is a 1.5 dp line in `chart-axis-label` at 85 %. Then `LineLegend` (16 × 10 dp line swatches: series, "Snitt", "Glidande snitt (3)"), and the caption in body-small `on-surface-variant`: the latest bucket the range does not cut short (a whole month or ISO week) against the average, "Oktober 2026: 12 % över snittet" / "… under snittet" / "…: på snittet" when the whole percent (half-up) is 0 (`analytics_vs_average_*`). Overlays and caption are omitted when the average is 0 or the range has one bucket; the caption is omitted when no bucket is whole. A single bucket shows one centred point and no average lines; an empty range shows only the 0 baseline and "Inga utgifter i den här perioden" (`analytics_empty`). TalkBack: the chart is one node, "Resor över tid, maj–okt 2026: från 860 kronor i maj 2026 till 9 600 kronor i oktober 2026, totalt 37 238 kronor, snitt 6 206 kronor." (`analytics_summary_line`; one bucket `analytics_summary_line_single`).
4. `SegmentedGroup` of `ExpenseRow`s, newest first.
Goldens (no catalogue frame): `s12_category`, `s12_category_groceries`, `s12_category_week`, `s12_category_empty_range`, `s12_account`, `s12_long`; components `line_chart`, `line_chart_variants`.
### S-13 Settings hub (`:feature:settings`)
**Layout** (app bar "Inställningar"; column gap 12; bottom navigation with Inställningar selected):