DataHistoryGraph

components/data/HistoryGraph.vue

Pictogram-style history chart — one unit-block lane per series, side by side per time column (or continuous bars past a density threshold), with an inline swatch legend. Opt-in: `stacked` draws the series bottom-up in one column a day; `maxHeight` caps the chart's height by coarsening the unit (design c110).

Mon
Tue
Wed
Thu
Fri
Sat
Sun
ChatGPT
claude
grok
Scenarios
7-day · 3 series · weekday labels
Mon
Tue
Wed
Thu
Fri
Sat
Sun
ChatGPT
claude
grok
30-day · 2 series · day-number labels
13
15
17
19
21
23
25
27
29
31
2
4
6
8
10
Desktop app
CLI
60-day · 1 series · date labels (auto → bar mode)
Jul 14
Jul 18
Jul 22
Jul 26
Jul 30
Aug 3
Aug 7
Aug 11
Aug 15
Aug 19
Aug 23
Aug 27
Aug 31
Sep 4
Sep 8
Sessions
Single spike — unit-scaling ceiling
Sat
Sun
Mon
Tue
Wed
Thu
Fri
Desktop app
CLI
Sparse/empty week
Sat
Sun
Mon
Tue
Wed
Thu
Fri
ChatGPT
claude
grok
Not collected — Wed (values: null), Sat (missing); Fri a real day of zeros
Mon
Tue
Wed
Thu
Fri
Sat
Sun
ChatGPT
claude
grok
First run — no day collected yet
Mon
Tue
Wed
Thu
Fri
Sat
Sun
ChatGPT
claude
grok
Forced bar mode, legend below
13
15
17
19
21
23
25
27
29
31
2
4
6
8
10
Desktop app
CLI
Stacked — 30-day · 2 series in one column a day
13
15
17
19
21
23
25
27
29
31
2
4
6
8
10
Desktop app
CLI
Stacked + maxHeight 64 — bars, the spike day still inside the cap
13
15
17
19
21
23
25
27
29
31
2
4
6
8
10
Desktop app
CLI
maxHeight 48 — single spike, blocks
Sat
Sun
Mon
Tue
Wed
Thu
Fri
Desktop app
CLI
ChatGPT
claude
grok
components/data/HistoryGraph.vue

Usage

<DataHistoryGraph :data = "required" :labelFormat = "null (auto)" :maxBlocksPerColumn = "12" :mode = "auto" :legendBelow = "false" :blockSize = "6" :blockGap = "2" :dayGap = "8" :labelSize = "11" :stacked = "false" :maxHeight = "maxHeight" />

Props

PropTypeDefaultDescription
dataObjectrequired{ series: [{ key, label, color? }], points: [{ date, values: { [seriesKey]: number } }] }. Dates are real Date/ISO values — the component derives the axis label, not the caller.
labelFormat'weekday' | 'day' | 'date'null (auto)X-axis label style. Auto-detected from range length when omitted: <=7 points -> weekday, <=31 -> day number, else -> date.
maxBlocksPerColumnNumber12Unit-scaling cap — the "nice" step (1/2/5 x10^n) per block is chosen so the tallest single-series lane never exceeds this many blocks. Series render side by side, not stacked, so this caps each lane independently.
mode'blocks' | 'bars' | 'auto''auto'Render as discrete unit blocks, continuous bars, or auto-switch to bars past ~40 columns where individual blocks would no longer read.
legendBelowBooleanfalseRender the legend below the columns in a horizontal row instead of beside them.
blockSizeNumber6Unit block width/height in px.
blockGapNumber2Gap between stacked blocks (vertical) and between series lanes within a day (horizontal), in px.
dayGapNumber8Horizontal gap between day columns in px.
labelSizeNumber11X-axis label font size in px. Scales the label-driven minimum column width to match; the legend label font size follows at labelSize + 1.
stackedBooleanfalseOne column per day, one lane wide: the series stack bottom-up in series order, each part its own color, so a small share stays visible on top of a big one (compliant / non-compliant). The unit scale follows the day's total, not the tallest lane. Blocks and bars; the hover payload is unchanged (design c110).
maxHeightNumbernullA px cap on the chart area. The block cap becomes what fits (maxHeight / (blockSize + blockGap), under maxBlocksPerColumn) and the nice unit steps up until the tallest column as drawn — rounded parts, stacked totals, a bar's unrounded height — fits; never clipped (design c110).

Related

Qpoint Brand Style Guide