GrChartBar

  • charts
  • charts
  • API · 37 props
  • ⌨ keys
import { GrChartBar } from '@feugene/granularity-charts/components/GrChartBar'

Overview

When to take it

  • how much every category has — orders by region, errors by service, revenue by product: columns are compared by eye more precisely than in any other type;
  • two or three metrics per category side by side — the series stand as a group, and groupPadding separates the groups;
  • the composition of every category — stacked folds the series into one column;
  • the shares inside a category — stacked: '100%' normalises the column to one, and the proportions are compared rather than the quantities;
  • long names of categories — orientation: 'horizontal' lays the bars sideways, and a label is read as a line rather than as a slanted tail;
  • there are few categories — the vertical layout holds fifteen or twenty, beyond that the labels of the axis run into one another; the horizontal one counts height rather than width and calmly takes thirty or forty rows.

When to take something else

NeedTake
Show the course of a value over timeGrChartLine
Show the whole and the contribution of its parts over timeGrChartArea
Show the composition of one wholeGrChartPie
Compare the shape of a profile across several axesGrChartRadar
Compare the rows of a table with one anotherGrDataTable + GrSparkline

Hundreds of categories are not a chart but a list: the room runs out before the reader’s patience does (in the vertical layout it is the width, in the horizontal one the height of the page). Such data is shown as a table with sorting, and the top of it with columns.

Examples 4 each with a live demo and its code

  • Plan against fact, side by side

    Grouped bars answer «how much each»: both start at zero and the difference is read by height — stacking them would make plan plus fact, a number that means nothing. group-padding separates the regions, bar-radius rounds the far end only, and point-click opens the region in the panel below.

    Q3 sales: plan against factThousands of euros by region · click a region
    Chart data
    XPlanFact
    North120132
    South9588
    East8084
    West11097
    Central7076
    NorthPlan €120kFact €132k+10.0%
  • What every week is made of

    stacked makes the height of a column the week’s total and its segments the channels — the shift from email to chat is visible without a second chart. A new team is fetched again, and loading keeps the frame instead of showing the old picture; show-grid="y" keeps only the lines that are read.

    Support tickets per weekChat grows while email and phone shrink
    Chart data
    XEmailChatPhone
    W3321214064
    W3419815658
    W3523017161
    W3624516855
    W3722119052
    W3820521449
    W3919023644
    W4018425141
  • Long names lie on their side

    orientation="horizontal" gives «Delivered after the promised date» a whole line instead of a tilted, clipped tail, and keeps the ranking top to bottom. In a ranking every bar matters, so dim-inactive is off; one series needs no legend, and the tooltip slot adds the share of all refunds.

    Refund reasons, September188 refunds, most frequent first
    Chart data
    XRefunds
    Ordered by mistake64
    Arrived damaged in transit41
    Customer changed their mind37
    Colour differs from the photo22
    Missing parts or screws15
    Delivered after the promised date9
  • Money and units on two axes

    Thousands of euros and thousands of orders cannot share a scale. dual-axis with y-domain-right and y-tick-format-right puts orders on the right; it is deliberate because two axes can make any pair look correlated, so both domains start at zero. Vertical bars only — a horizontal layout has no room for a second value axis.

    Revenue and ordersOrders grow faster than revenue: the average basket is getting smaller
    Chart data
    XRevenue, € thousands (left axis)Orders (right axis)
    Apr3121,840
    May3381,910
    Jun3511,875
    Jul3722,050
    Aug3862,190
    Sep3992,240

API generated from the package · cannot drift

Props

PropTypedefaultDescription
xScaleGrChartScaleKind | undefinedundefinedType of the X axis. Not set — inferred from the data; for bars that is usually categories.
stackedboolean | "100%" | undefinedfalse`true` — a stack, `'100%'` — a stack with the column normalized to one.
heightnumber | undefinedundefinedThe canvas height in pixels; 256 by default. The data, the loading state and the empty state all take up the same height.
widthnumber | undefined640Declared width: the first render goes from it, after that the width is measured.
yDomainreadonly [number | null, number | null] | undefinedundefinedThe bounds of the value axis `[min, max]`; `null` in place of a bound — it is taken from the data.
referencesreadonly GrChartReference[] | undefinedundefinedReference lines and bands: a threshold, a plan, an acceptable corridor. Not a series and never counted as one: it does not get into the legend, does not spend a palette index, does not join the stack and goes into the hidden table as a note, not as a data row.
includeReferencesInDomainboolean | undefinedfalseInclude the references in the axis domain. Off by default, and deliberately so: a threshold of `1.0` with data around `0.03` would stretch the axis so far that the data itself would collapse into a line.
dualAxisboolean | undefinedfalseShow a second value axis on the right. Without it, series with `axis: 'right'` land on the left one. Turning it on is deliberate: two axes let you fit any two series to a visible correlation, and that must be a decision of the chart author, not a side effect of a field in the data.
yDomainRightreadonly [number | null, number | null] | undefinedundefinedThe bounds of the right value axis with `dualAxis`; `null` in place of a bound — it is taken from the data.
yTickFormatRight((value: number) => string) | undefinedundefinedThe tick label of the right axis. Unset — a number formatted by the locale.
valueFormatRightGrChartNumberFormat | undefinedundefinedThe value format of the right-axis series in the tooltip, the hidden table and announcements.
xTickCountnumber | undefinedundefinedThe desired number of ticks on the X axis; 6 by default. For categories — the cap on the number of labels (unset — 12). Labels that do not fit the width are thinned out.
yTickCountnumber | undefined5The desired number of ticks on the value axis; 5 by default. The step stays "round", so there may be one tick fewer.
xTickFormatChartTickFormat | undefinedundefinedThe tick label of the X axis: `(value, kind) => string`. Unset — the category, or a date or number formatted by the locale.

Slots

SlotTypeDescription
tooltip{ active: GrChartActivePoint; formatValue: (value: number | null) => string; }Custom content for the tooltip panel instead of the standard one; the data of the active position comes in the slot props.
legend{ series: readonly NormalizedSeries[]; toggle: (id: string) => void; }A custom legend instead of the standard one. It is not drawn for an empty chart.
emptyanyA custom placeholder for an empty chart instead of "No data"; it takes up the same `height`.
headeranyA header above the chart, inside its root — above a legend placed on top.

Events

EventSignatureDescription
update:hiddenSeries(event: "update:hiddenSeries", value: string[]): void—
update:activeIndex(event: "update:activeIndex", value: number | null): void—
pointClick(event: "pointClick", value: GrChartActivePoint): void—
pointHover(event: "pointHover", value: GrChartActivePoint | null): void—
legendToggle(event: "legendToggle", value: { seriesId: string; hidden: boolean; }): void—

Methods

MethodsSignatureDescription
elementHTMLElement | nullThe root element — for measurements and scrolling in the consumer.

Types

Every exported type of the subpath, grouped by the file it lives in. Names, generics and bodies come from the package’s declaration files and cannot drift.

Import
import type { GrChartBarConfigurableProps, GrChartSize, GrChartBarProps } from '@feugene/granularity-charts/components/GrChartBar'

Filedist/types/components/GrChartBar/config.d.ts

grChartBarConfigconst
const grChartBarConfig: import("@feugene/granum/contract").GranumComponentDescriptor<"GrChartBar">;

Filedist/types/components/GrChartBar/defaults.d.ts

GrChartBarConfigurablePropsinterface
interface GrChartBarConfigurableProps {
    size: GrChartSize;
    orientation: ChartOrientation;
    height: number;
    barRadius: number;
    groupPadding: number;
    dimInactive: boolean;
    showGrid: 'both' | 'x' | 'y' | 'none';
    showLegend: boolean | 'auto';
    legendPosition: 'top' | 'bottom';
    tooltip: boolean;
    dataTable: 'hidden' | 'visible' | 'off';
    dataTableMaxRows: number | 'auto';
}

Filedist/types/components/GrChartFrame/chartFrameStyles.d.ts

GrChartSizetype
type GrChartSize = GrComponentSize;

Filedist/types/components/GrChartBar/safelist.d.ts

grChartBarSafelistconst
const grChartBarSafelist: string[];

Filedist/types/components/GrChartBar/GrChartBar.vue.d.ts

GrChartBarPropsinterface
interface GrChartBarProps {
    series: readonly GrChartSeries[] | readonly number[];
    xScale?: GrChartScaleKind;
    orientation?: ChartOrientation;
    stacked?: boolean | '100%';
    groupPadding?: number;
    barRadius?: number;
    dimInactive?: boolean;
    height?: number;
    width?: number;
    yDomain?: readonly [number | null, number | null];
    references?: readonly GrChartReference[];
    includeReferencesInDomain?: boolean;
    dualAxis?: boolean;
    yDomainRight?: readonly [number | null, number | null];
    yTickFormatRight?: (value: number) => string;
GrChartBarEmitsinterface
interface GrChartBarEmits {
    (e: 'update:hiddenSeries', value: string[]): void;
    (e: 'update:activeIndex', value: number | null): void;
    (e: 'pointClick', value: GrChartActivePoint): void;
    (e: 'pointHover', value: GrChartActivePoint | null): void;
    (e: 'legendToggle', value: {
        seriesId: string;
        hidden: boolean;
    }): void;
}

Accessibility

APG pattern

Серия читается той же парой стрелок; активная категория подсвечивается приглушением соседей

Usage from the library prose

Horizontal: bars sideways, the axes named by the data
vue
<GrChartBar :series="series" orientation="horizontal" aria-label="Revenue by product" />

Only the layout changes: the stack, the group, '100%', the references, the legend, the tooltip and the hidden table work in exactly the same way.

The axes are named by the data rather than by the screen. yDomain, yTickFormat and yTickCount are always the value axis, wherever it lies; xTickFormat is always the labels of the categories. With showGrid: 'y' the value lines in the horizontal layout run vertically: the grid follows its own axis rather than the direction on the screen.

The alternative — introducing paired horizontalTickFormat and the rest — would give two props with one meaning and the question “and which of them is in charge right now” on every page. One imprecise name is cheaper than two precise ones.

The keyboard follows the eyes: ↓/↑ walk the categories from top to bottom, and ←/→ switch the series being read inside a category.

A category name reads as a line. The label column grows to fit the longest formatted name — up to 40% of the chart width, but no narrower than 96px. An ellipsis appears only beyond this ceiling, and the full name stays in the label’s <title>, in the tooltip and in the hidden table.

What the horizontal layout cannot do

dualAxis with orientation: 'horizontal' is switched off: the second value axis would become the top one, and the layout has no top margin. The prop does not fail — it simply does not apply, all of the series sit on one axis, and a warning is printed in dev. If two scales are needed, take the vertical layout.

Zero on the axis is not switchable

There is nothing to switch off in includeZero for columns: a bar is drawn from the baseline, and if the base is not zero, the length of the bar stops being the quantity. In a line chart there is a choice — there the course is read rather than the length.

Dimming instead of hiding

dimInactive dims the inactive series under the cursor instead of removing them. A removed series would shift its neighbours and change the width of the bars — the drawing would “move” from a single movement of the mouse.

A threshold is drawn as a reference, not as a series

A plan, a norm and the limit of the acceptable are the references prop rather than a series made of a constant: a constant series would enter the legend, would stretch the axis and would travel into the table as data. The details — ../model.md, the section “A reference is not a series”.

A stack and a group are different questions

A group answers “how much each has”, a stack “what each consists of”. Mixing them in one chart is not allowed: the reader does not know whether to compare the heights of the columns or the heights of the segments. If both answers are needed, that is two charts.

Two axes are a deliberate decision

Series of different orders (money and units) are readable on one chart only with two axes, and two axes let any pair be fitted to a visible correlation. That is why the axis: 'right' of a series does not work until dualAxis is switched on. The invariants — ../model.md, the section “The second value axis”.