GrChartBullet

  • charts
  • charts
  • API · 23 props
import { GrChartBullet } from '@feugene/granularity-charts/components/GrChartBullet'

Overview

When to take it

  • a metric with thresholds that are already in the data — warning and critical arrive from the backend and are usually drawn nowhere: “bad” is visible, “how bad” is not;
  • the plan and the fact side by side — the notch of the target and the bar of the value are read at a glance, without subtracting in one’s head;
  • several metrics in a row — a bullet takes up a line, and a dozen metrics stand as a list comparable vertically;
  • room is expensive — a card, a table row, the header of a dashboard: the same meaning as a dial in a fifth of the area;
  • the quantity may be missing — value: null draws the target and the ranges without a bar rather than a zero.

When to take something else

NeedTake
Compare the quantities of several categories with one anotherGrChartBar
Show the course of a quantity over timeGrChartLine
Show the share of completion of a single taskGrProgressCircle
Show one number large, with a trendGrStatistic

Examples 4 each with a live demo and its code

  • Latency objectives, one service per row

    ranges paint the track in bands — good, tolerable, bad — and target draws the objective across it: «182» alone says little, «182 of 200, inside the good band» says it all. A bullet takes one line, so a table of services fits where a single gauge would stand. A service without traffic gets value: null: the mark and the bands stay, the bar does not — no data is not zero. value-click opens the service.

    Latency objectives · p95, last hourThe mark is the objective; the bands are good, tolerable and bad
    Checkout API
    Chart data
    MetricValueTarget
    Checkout API182200
    Ranges: 0 to 200; 200 to 300; 300 to 400
    182 ms
    Search
    Chart data
    MetricValueTarget
    Search96150
    Ranges: 0 to 150; 150 to 250; 250 to 300
    96 ms
    Invoices
    Chart data
    MetricValueTarget
    Invoices341250
    Ranges: 0 to 250; 250 to 350; 350 to 450
    341 ms
    Exports
    Chart data
    MetricValueTarget
    Exportsno value900
    Ranges: 0 to 900; 900 to 1,500; 1,500 to 2,000
    no traffic

    Click a bar to open the service.

  • Quota, where more is better

    The default bands run from good to bad as values grow — right for latency, wrong for a quota. range-colors paints them band by band: red under 70 %, amber up to the quota, green above it. color sets the tone of the value bar, and size="xs" keeps four people in a small card.

    Quota attainment · OctoberTarget is 100 % of the personal quota
    Grace Wu114%
    Chart data
    MetricValueTarget
    Grace Wu1.141.00
    Ranges: 0.00 to 0.70; 0.70 to 1.00; 1.00 to 1.30
    Omar Haddad96%
    Chart data
    MetricValueTarget
    Omar Haddad0.961.00
    Ranges: 0.00 to 0.70; 0.70 to 1.00; 1.00 to 1.30
    Ines Novak81%
    Chart data
    MetricValueTarget
    Ines Novak0.811.00
    Ranges: 0.00 to 0.70; 0.70 to 1.00; 1.00 to 1.30
    Linus Ray58%
    Chart data
    MetricValueTarget
    Linus Ray0.581.00
    Ranges: 0.00 to 0.70; 0.70 to 1.00; 1.00 to 1.30
  • A budget that is already overspent

    Marketing spent more than its whole budget: the bar stops at max with an overflow marker instead of being clipped silently, and the real figure stays in the tooltip, the hidden table and the announcement. The tooltip slot adds the share of the budget; aria-description explains what the mark means.

    Budgets after nine monthsThe mark is the share of the year gone — 75 % of the budget
    Engineering€1420k of €2000k
    Chart data
    MetricValueTarget
    Engineering1,4201,500
    Ranges: 0 to 1,500; 1,500 to 1,800; 1,800 to 2,000
    Marketing€980k of €800k
    Chart data
    MetricValueTarget
    Marketing980600
    Ranges: 0 to 600; 600 to 720; 720 to 800
    Sales€610k of €900k
    Chart data
    MetricValueTarget
    Sales610675
    Ranges: 0 to 675; 675 to 810; 810 to 900
  • Warehouse tiles, read bottom to top

    A narrow dashboard tile has height, not width: orientation="vertical" keeps the same value, target and bands, read bottom to top. label names the metric for the tooltip and the data table; the caption on the tile is the page’s own, because the page knows where the tile stands.

    Warehouse capacityPallet places in use · target 80 %
    Chart data
    MetricValueTarget
    Hamburg812800
    Ranges: 0 to 850; 850 to 950; 950 to 1,000
    Hamburg81% full812 of 1000
    Chart data
    MetricValueTarget
    Bremen640720
    Ranges: 0 to 765; 765 to 855; 855 to 900
    Bremen71% full640 of 900
    Chart data
    MetricValueTarget
    Rotterdam1,170960
    Ranges: 0 to 1,020; 1,020 to 1,140; 1,140 to 1,200
    Rotterdam98% full1170 of 1200

API generated from the package · cannot drift

Props

PropTypedefaultDescription
heightnumber | undefinedundefinedThe canvas height in pixels; 48 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.
valueFormatGrChartNumberFormat | undefinedundefinedThe value format in the tooltip, the hidden table and announcements: decimal places and separators.
tooltipboolean | undefinedundefinedThe panel with the values under the cursor; `true` by default. `false` hides only the panel: the cursor, the active marks, `update:activeIndex` and `pointHover` keep working while the chart is interactive.
loadingboolean | undefinedfalseLoading: a skeleton of the same height stands in place of the plot area, and the space for the axes is already reserved.
emptyboolean | undefinedundefinedThe empty state, set explicitly. Unset — the chart is empty when there is nothing to draw.
emptyTextstring | undefinedundefinedThe placeholder text of an empty chart. Unset — "No data" from the package translations.
dataTable"hidden" | "visible" | "off" | undefinedundefinedThe data table: `hidden` (the default) — for the screen reader only, `visible` — under the chart, `off` — no table.
interactiveboolean | undefinedtrue`false` — the chart becomes a picture: no focus, no tooltip, no keyboard.
size"md" | "xs" | "sm" | "lg" | undefinedundefinedThe type size of the labels on the core control scale; by default — the size from `GrConfigProvider`.
localestring | undefinedundefinedThe BCP-47 locale for numbers and dates. Unset — the language of the application translations, otherwise `en`.
ariaLabelstring | undefinedundefinedThe accessible name of the chart. Unset — a summary built from `label`, the measure and the target.
ariaDescriptionstring | undefinedundefinedA description for the screen reader — the conclusion the chart is shown for; it adds to the summary rather than replacing it.
dataTableMaxRowsnumber | "auto" | undefinedundefinedThe row ceiling of the hidden data table. `'auto'` (the default) — as many rows as can be read; above the ceiling the table goes by an even sample and says so with a note, while point-by-point completeness stays with the keyboard. A number sets its own ceiling, `Infinity` removes it altogether, `dataTable: 'off'` removes the table entirely — which of these is needed is up to the application.

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.
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
valueClick(event: "valueClick", value: number | null): 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 { GrChartBulletConfigurableProps, GrChartSize, GrChartBulletProps } from '@feugene/granularity-charts/components/GrChartBullet'

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

grChartBulletConfigconst
const grChartBulletConfig: import("@feugene/granum/contract").GranumComponentDescriptor<"GrChartBullet">;

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

GrChartBulletConfigurablePropsinterface
interface GrChartBulletConfigurableProps {
    size: GrChartSize;
    height: number;
    orientation: 'horizontal' | 'vertical';
    tooltip: boolean;
    dataTable: 'hidden' | 'visible' | 'off';
    dataTableMaxRows: number | 'auto';
}

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

GrChartSizetype
type GrChartSize = GrComponentSize;

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

grChartBulletSafelistconst
const grChartBulletSafelist: string[];

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

GrChartBulletPropsinterface
interface GrChartBulletProps {
    value: number | null;
    target?: number;
    ranges?: readonly number[];
    max?: number;
    min?: number;
    rangeColors?: readonly string[];
    color?: string;
    orientation?: 'horizontal' | 'vertical';
    label?: string;
    height?: number;
    width?: number;
    valueFormat?: GrChartNumberFormat;
    tooltip?: boolean;
    loading?: boolean;
    empty?: boolean;
    emptyText?: string;
    dataTable?: 'hidden' | 'visible' | 'off';
    interactive?: boolean;
    size?: GrChartSize;
    locale?: string;
    ariaLabel?: string;
    ariaDescription?: string;
    dataTableMaxRows?: number | 'auto';
}
GrChartBulletEmitsinterface
interface GrChartBulletEmits {
    (e: 'valueClick', value: number | null): void;
}

Usage from the library prose

There is deliberately no dial in the package

A gauge spends a lot of room on little data and reads poorly quantitatively: from a needle one takes “roughly in the middle” rather than a number. A bullet solves the same task with a bar, and when there are several such metrics it is also comparable vertically, which dials cannot do at all.

Three weights that do not argue

The ranges are the background, the value a narrow bar on top, the target a notch across. The thickness of the value bar is set by the --gr-chart-bullet-value-width token rather than by the geometry: the bar is drawn with a <line>, and the theme changes its weight without touching the track.

Colours of your own for the ranges are set by rangeColors — one per band, from “good” to “bad”. There is always one band more than there are boundaries, even if a boundary went off the scale: such a boundary is clamped rather than thrown away, otherwise the colours would move onto the neighbouring bands.

value: null is not a zero

If there is no quantity, the bar is not drawn, the notch of the target remains and there is a dash in the table. For a cost that is the literal case: no write-offs means no cost, and a zero would lie here.

Together with the value the meter role disappears as well: it requires aria-valuenow, and without a quantity there is none — a role left in place would give a violation of serious level.

A value beyond the scale is not truncated silently

The bar runs into the edge and gets an overflow marker, and the real quantity goes into the tooltip, into the hidden table and into the announcement. Truncating it quietly would mean showing a different number.

The role is meter rather than progressbar

progressbar is about the completion of a task that comes to an end; here there is a quantity that simply is. slider fits even less: the value is not editable. Besides aria-valuenow/valuemin/valuemax the overlay carries an aria-valuetext with a human wording (“0.031 of 0.05, target 0.04”) — one number without units and without the target would say less than a sighted person sees. The ranges are announced in the description of the chart: the coloured zones would otherwise exist only for the sighted.

Limits

There are no several values in one track: a bullet compares one quantity with one target. The component does not draw a needle or the “gathering” of the value with an animation either.