Components

Chart

Bars, a line, an area or a donut drawn to scale, with a tooltip, a legend, keys through the points and its data as a table.

height
grid
indicator
scheme
direction

Bars #

Visitors

January to June

Visitors
CategoryDesktopMobile
January18680
February305200
March237120
April73190
May209130
June214140
export const ChartStyle = forSlots(ChartSlots)(Recipes.Chart({}))

/** A chart fills its container's width: the page gives it one. */
const chartFrame = <Message>(h: HtmlBuilder<Message>, chart: Html): Html =>
  h.div([h.Style({ inlineSize: 'min(36rem, 100%)' })], [chart])

export const visitors = {
  categories: ['January', 'February', 'March', 'April', 'May', 'June'],
  series: [
    { name: 'Desktop', values: [186, 305, 237, 73, 209, 214] },
    { name: 'Mobile', values: [80, 200, 120, 190, 130, 140] },
  ],
}

// The lit point is the chart's Model, folded with `Chart.update`.
export const chartBar = <Message>(
  style: NamedStyle<typeof ChartSlots>,
  model: Chart.Model,
  onMessage: (message: Chart.Message) => Message,
  h: HtmlBuilder<Message>,
): Html =>
  chartFrame(
    h,
    Chart.view(
      {
        id: 'preview-visitors',
        label: 'Visitors',
        description: 'January to June',
        kind: 'bar',
        ...visitors,
        model,
        onMessage,
        style,
      },
      h,
    ),
  )

A line #

Visitors
Visitors
CategoryDesktopMobile
January186 k80 k
February305 k200 k
March237 k120 k
April73 k190 k
May209 k130 k
June214 k140 k
export const chartLine = <Message>(
  style: NamedStyle<typeof ChartSlots>,
  model: Chart.Model,
  onMessage: (message: Chart.Message) => Message,
  h: HtmlBuilder<Message>,
): Html =>
  chartFrame(
    h,
    Chart.view(
      {
        id: 'line-visitors',
        label: 'Visitors',
        kind: 'line',
        ...visitors,
        model,
        onMessage,
        format: value => `${value} k`,
        style,
      },
      h,
    ),
  )

An area #

Sign-ups
Sign-ups
CategorySign-ups
Mon42
Tue58
Wed51
Thu77
Fri69
Sat31
Sun24
export const SmallChartStyle = forSlots(ChartSlots)(Recipes.Chart({ height: 'sm', grid: 'zero' }))

// One series: no legend, the title names it.
export const chartArea = <Message>(
  style: NamedStyle<typeof ChartSlots>,
  model: Chart.Model,
  onMessage: (message: Chart.Message) => Message,
  h: HtmlBuilder<Message>,
): Html =>
  chartFrame(
    h,
    Chart.view(
      {
        id: 'area-signups',
        label: 'Sign-ups',
        kind: 'area',
        categories: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'],
        series: [{ name: 'Sign-ups', values: [42, 58, 51, 77, 69, 31, 24] }],
        model,
        onMessage,
        style,
      },
      h,
    ),
  )

A donut #

Visitors by browser
Visitors by browser
Value
Chrome275
Safari200
Firefox187
Edge173
Other90
export const chartDonut = <Message>(
  style: NamedStyle<typeof ChartSlots>,
  model: Chart.Model,
  onMessage: (message: Chart.Message) => Message,
  h: HtmlBuilder<Message>,
): Html =>
  chartFrame(
    h,
    Chart.view(
      {
        id: 'donut-browsers',
        label: 'Visitors by browser',
        kind: 'donut',
        slices: [
          { name: 'Chrome', value: 275 },
          { name: 'Safari', value: 200 },
          { name: 'Firefox', value: 187 },
          { name: 'Edge', value: 173 },
          { name: 'Other', value: 90 },
        ],
        totalLabel: 'Visitors',
        model,
        onMessage,
        style,
      },
      h,
    ),
  )

Its data, shown #

Visitors
Visitors
MonthDesktopMobile
January18680
February305200
March237120
April73190
May209130
June214140
// The table assistive technology reads, shown for every reader.
export const chartTable = <Message>(
  style: NamedStyle<typeof ChartSlots>,
  model: Chart.Model,
  onMessage: (message: Chart.Message) => Message,
  h: HtmlBuilder<Message>,
): Html =>
  chartFrame(
    h,
    Chart.view(
      {
        id: 'table-visitors',
        label: 'Visitors',
        kind: 'bar',
        ...visitors,
        categoryLabel: 'Month',
        table: 'shown',
        model,
        onMessage,
        style,
      },
      h,
    ),
  )

A series keeps its color #

Mobile visitors
Mobile visitors
CategoryMobile
January80
February200
March120
April190
May130
June140
// Mobile names its color, so hiding Desktop does not repaint it.
export const chartColors = <Message>(
  style: NamedStyle<typeof ChartSlots>,
  model: Chart.Model,
  onMessage: (message: Chart.Message) => Message,
  h: HtmlBuilder<Message>,
): Html =>
  chartFrame(
    h,
    Chart.view(
      {
        id: 'colors-visitors',
        label: 'Mobile visitors',
        kind: 'bar',
        categories: visitors.categories,
        series: [{ name: 'Mobile', values: [80, 200, 120, 190, 130, 140], color: 2 }],
        legend: true,
        model,
        onMessage,
        style,
      },
      h,
    ),
  )

Usage

Each example above names the function that drew it, over the theme module Create a theme writes. The preview passes it the style picked above instead of its default.

The recipe ↗This code ↗