Pantrix UI

Waterfall Chart

A waterfall chart: how an amount comes about, step by step. Each bar starts where the one before it ended, going up for what a row adds and down for what it takes away, and a total stands on nought and shows what the steps before it come to.

import com.pantrixui.components.chart.waterfall.PantrixWaterfallChart

Examples

From opening to closing

A quarter's cash flow: the opening amount, income, expenses and the closing. The opening is a total with a value of its own: the count starts from it. The closing has no value: it shows what the steps come to.

Move the pointer over a bar, or touch the bar: the tooltip writes the step with its sign. The thin lines between the bars show that each step starts where the one before it ended.

PantrixWaterfallChart(
    data = QUARTER_CASH,
    x = { it.name },
    y = { it.amount },
    name = "Cash (thousand TL)",
    total = { it.total },
    description = "A quarter's cash flow, from opening to closing",
    tagKey = "plain"
)

private data class CashMove(
    val name: String,
    val amount: Int?,
    val total: Boolean = false
)

private val QUARTER_CASH = listOf(
    CashMove("Opening", amount = 420, total = true),
    CashMove("Sales", amount = 310),
    CashMove("Services", amount = 120),
    CashMove("Salaries", amount = -260),
    CashMove("Rent", amount = -90),
    CashMove("Tax", amount = -70),
    CashMove("Closing", amount = null, total = true)
)

Subtotals

Subtotals along the way: at the end of each quarter, what has been reached so far. A subtotal stands on zero, and the count goes on after it from where it was.

PantrixWaterfallChart(
    data = HALF_YEAR_CASH,
    x = { it.name },
    y = { it.amount },
    name = "Cash (thousand TL)",
    total = { it.total },
    description = "Monthly change in cash over half a year, with a subtotal at the end of each quarter",
    tagKey = "subtotals"
)

private data class CashMove(
    val name: String,
    val amount: Int?,
    val total: Boolean = false
)

private val HALF_YEAR_CASH = listOf(
    CashMove("Jan", amount = 180),
    CashMove("Feb", amount = -60),
    CashMove("Mar", amount = 140),
    CashMove("Q1", amount = null, total = true),
    CashMove("Apr", amount = -110),
    CashMove("May", amount = 90),
    CashMove("Jun", amount = 210),
    CashMove("Q2", amount = null, total = true)
)

The values at the ends of the bars

What each bar stands for is written at its end (labels): over a step that adds and under a step that takes away, with its sign; over a total, as it is.

A label that has no room is left out; the value stays in the tooltip and for the screen reader.

PantrixWaterfallChart(
    data = QUARTER_CASH,
    x = { it.name },
    y = { it.amount },
    name = "Cash (thousand TL)",
    total = { it.total },
    labels = ChartDataLabels(),
    description = "A quarter's cash flow, with its values",
    tagKey = "labels"
)

private data class CashMove(
    val name: String,
    val amount: Int?,
    val total: Boolean = false
)

private val QUARTER_CASH = listOf(
    CashMove("Opening", amount = 420, total = true),
    CashMove("Sales", amount = 310),
    CashMove("Services", amount = 120),
    CashMove("Salaries", amount = -260),
    CashMove("Rent", amount = -90),
    CashMove("Tax", amount = -70),
    CashMove("Closing", amount = null, total = true)
)

Horizontal

The same quarter, horizontal (horizontal = true): the items are set down the side of the chart, and each bar starts where the one above it ended. Where the items have long names, or are many, a horizontal waterfall is easier to read.

PantrixWaterfallChart(
    data = QUARTER_CASH,
    x = { it.name },
    y = { it.amount },
    name = "Cash (thousand TL)",
    total = { it.total },
    horizontal = true,
    labels = ChartDataLabels(),
    description = "A quarter's cash flow, in horizontal bars",
    tagKey = "horizontal_steps"
)

private data class CashMove(
    val name: String,
    val amount: Int?,
    val total: Boolean = false
)

private val QUARTER_CASH = listOf(
    CashMove("Opening", amount = 420, total = true),
    CashMove("Sales", amount = 310),
    CashMove("Services", amount = 120),
    CashMove("Salaries", amount = -260),
    CashMove("Rent", amount = -90),
    CashMove("Tax", amount = -70),
    CashMove("Closing", amount = null, total = true)
)

Your own colours

The two colours of the steps come from the chart's colours (the rising and falling fields of ChartColors), and the totals' colour from the series itself (color). Turn the switch: the lines between the bars go away (connectors).

Colour should not carry the meaning alone: which way a step goes is also read from where its bar starts and ends, and from the sign of its value.

var connectors by remember { mutableStateOf(true) }
val theme = PantrixTheme.colors
PantrixSwitchButton(
    checked = connectors,
    onCheckedChange = { connectors = it },
    label = "Connecting lines",
    tagKey = "waterfall_connectors"
)
PantrixWaterfallChart(
    data = QUARTER_CASH,
    x = { it.name },
    y = { it.amount },
    name = "Cash (thousand TL)",
    total = { it.total },
    connectors = connectors,
    color = theme.neutral.color,
    colors = ChartColors(rising = theme.info.color, falling = theme.warning.color),
    description = "A quarter's cash flow, in its own colours",
    tagKey = "own_colors"
)

private data class CashMove(
    val name: String,
    val amount: Int?,
    val total: Boolean = false
)

private val QUARTER_CASH = listOf(
    CashMove("Opening", amount = 420, total = true),
    CashMove("Sales", amount = 310),
    CashMove("Services", amount = 120),
    CashMove("Salaries", amount = -260),
    CashMove("Rent", amount = -90),
    CashMove("Tax", amount = -70),
    CashMove("Closing", amount = null, total = true)
)

Pressing a step

A press is handed the step and the level it starts from: the point's value is what the step adds and its from the level the step starts from; the two together are what is reached after the step. A total has no from. Press a bar: the line below writes what was pressed.

var pressed by remember { mutableStateOf<String?>(null) }
val strings = PantrixTheme.strings
PantrixWaterfallChart(
    data = QUARTER_CASH,
    x = { it.name },
    y = { it.amount },
    name = "Cash (thousand TL)",
    total = { it.total },
    onSelect = { point ->
        val start = point.from
        pressed = if (start == null) {
            "${point.label}: total ${point.text}"
        } else {
            "${point.label}: ${point.text}, then ${strings.decimal(start + point.value)}"
        }
    },
    description = "A quarter's cash flow",
    tagKey = "press"
)
PantrixText(text = pressed ?: "Press a bar")

private data class CashMove(
    val name: String,
    val amount: Int?,
    val total: Boolean = false
)

private val QUARTER_CASH = listOf(
    CashMove("Opening", amount = 420, total = true),
    CashMove("Sales", amount = 310),
    CashMove("Services", amount = 120),
    CashMove("Salaries", amount = -260),
    CashMove("Rent", amount = -90),
    CashMove("Tax", amount = -70),
    CashMove("Closing", amount = null, total = true)
)

The long form

The chart the short form stands for: a PantrixChart with one WaterfallSeries. The short form's horizontal = true is orientation = ChartOrientation.Horizontal in the long form, and the series carries its key and its name itself. A screen that wants the waterfall together with other things writes it this way: here a line for the closing target.

PantrixChart(
    series = listOf(
        WaterfallSeries(
            key = "cash",
            name = "Cash (thousand TL)",
            data = QUARTER_CASH,
            x = { it.name },
            y = { it.amount },
            total = { it.total }
        )
    ),
    orientation = ChartOrientation.Horizontal,
    marks = listOf(ChartReferenceLine(y = 500, label = "Target")),
    description = "A quarter's cash flow and the closing target, in horizontal bars",
    tagKey = "as_series"
)

private data class CashMove(
    val name: String,
    val amount: Int?,
    val total: Boolean = false
)

private val QUARTER_CASH = listOf(
    CashMove("Opening", amount = 420, total = true),
    CashMove("Sales", amount = 310),
    CashMove("Services", amount = 120),
    CashMove("Salaries", amount = -260),
    CashMove("Rent", amount = -90),
    CashMove("Tax", amount = -70),
    CashMove("Closing", amount = null, total = true)
)

API

data

A waterfall chart: how an amount comes about, step by step. Each bar starts where the one before it ended, going up for what a row adds and down for what it takes away, and a total stands on nought and shows what the steps before it come to.

ParameterTypeDefaultWhat it is
dataList<T>the rows, in the order their steps are counted.
x(T) -> Anywhere a row stands along the chart: a name, a day, anything that is equal to itself.
y(T) -> Number?what a row adds, under nought what it takes away, or null where it has no value. For a total, the amount the count starts again from, or null for what the steps before it come to.
nameStringwhat the amounts are of: what the tooltip calls them, and a screen reader the chart where it has no description.
modifierModifierModifierthe chart's modifier. A height here is the chart's.
total((T) -> Boolean)?nullwhether a row is a total. null, the chart has none.
connectorsBooleantruewhether a line leads from the end of each bar to the next.
colorColorColor.Unspecifiedthe colour the totals are drawn in. Left out, it is the first of the chart's colours.
horizontalBooleanfalsewhether the places go down the chart's side and the bars along it, and not along its foot and up.
labelsChartDataLabels?nullwhat each bar stands for written at its end, a step with its sign and a total as it is, where it has room. null writes none.
xAxisChartAxis?ChartAxis()the axis of the places, under the chart, or beside it where it is horizontal. null draws nothing of it.
yAxisChartAxis?ChartAxis()the axis of the values, beside the chart, or under it where it is horizontal. null draws nothing of it.
marksList<ChartMark>emptyList()the lines and the bands drawn in the plot besides the steps: a target, a limit.
tooltipChartTooltip?ChartTooltip()what is shown for the place that is pointed at. null shows none.
zoomChartZoom?nullwhat lets the reader look closer at a part of the places and move along the rest. null, which it is unless the chart is given one, shows all the places and no more.
exportChartExport?nullwhat lets the reader take the chart out of the app as a file: a picture, or a drawing that is sharp at any size. null, which it is unless the chart is given one, and the chart has no such button.
empty(@Composable () -> Unit)?nullwhat the chart shows in its plot while it has no value to draw, in the place of the theme's words for it ("No data"), which a screen reader is then not told either. null writes the words. A file the chart is exported as says them.
stateChartStaterememberChartState()what the reader points at: the chart's own where none is given.
descriptionString?nullwhat the chart shows, in a few words, for a screen reader.
onSelect((ChartPoint) -> Unit)?nullcalled with the step or the total that is pressed.
styleChartStyle?nullthis chart's own geometry.
colorsChartColors?nullthis chart's own colours, where they are not the theme's: rising and falling among them.
tagKeyString?nullthe chart's automation key. The chart is tagged as the PantrixChart it is, Chart_<tagKey>, and its parts and events are that chart's; its one series has the key steps.

ChartStyle

A chart's geometry: how tall it is where nothing says, how thick its lines and how large its marks are, and the room between its parts; and whether its fills have textures besides their colours.

ParameterTypeDefaultWhat it is
heightDpDp.UnspecifiedHow tall a chart's plot is, with its axes, where the chart's modifier gives it no height. A legend above or under the plot adds its own.
lineWidthDpDp.UnspecifiedThe width of a series' line.
markerSizeDpDp.UnspecifiedThe width and height of the mark on a point.
selectedMarkerSizeDpDp.UnspecifiedThe same of the mark on the point that is pointed at.
gridWidthDpDp.UnspecifiedThe width of the grid's lines, of the lines along the axes and of the line that marks the place pointed at. Zero draws none.
labelGapDpDp.UnspecifiedBetween the plot and what is written beside it and under it, and between that and an axis' title.
tickSpacingDpDp.UnspecifiedThe least room between two values marked along the axis of values: what decides how many are marked where the axis does not say.
legendGapDpDp.UnspecifiedBetween the legend and the chart it stands beside.
legendItemGapDpDp.UnspecifiedBetween one entry of the legend and the next.
swatchSizeDpDp.UnspecifiedHow wide the sample of a series is in the legend and in the tooltip: a short run of its line, or a patch of its fill.
swatchGapDpDp.UnspecifiedBetween that sample and the name after it.
tooltipShapeShapeSpecShapeSpec.UnspecifiedThe tooltip's corners: a step of the brand's shape scale, or a fixed shape.
tooltipHorizontalPaddingDpDp.UnspecifiedBetween the tooltip's sides and what it holds.
tooltipVerticalPaddingDpDp.UnspecifiedBetween its top and bottom and what it holds, and between its heading and rows.
tooltipGapDpDp.UnspecifiedBetween the place pointed at and the tooltip beside it.
tooltipElevationDpDp.UnspecifiedThe tooltip's shadow.
barGapDpDp.UnspecifiedBetween two bars that stand side by side at a place. Bars too thin for it stand closer.
barMaxWidthDpDp.UnspecifiedThe widest a bar is, however much room its place has: a chart of three places is not three slabs.
barRadiusDpDp.UnspecifiedHow round the corners of a bar's end are, the end away from nought.
sliceGapDpDp.UnspecifiedBetween two slices of a pie that lie side by side, the same from the pie's middle to its rim.
selectedSliceOffsetDpDp.UnspecifiedHow far the slice of a pie that is pointed at stands out past the pie's rim. The pie keeps this much room about itself, so that the slice has where to stand.
gaugeWidthDpDp.UnspecifiedHow thick the arc of a gauge is. An arc is no thicker than half its radius.
meterHeightDpDp.UnspecifiedHow tall the bar of a meter group is.
trackerHeightDpDp.UnspecifiedHow tall the blocks of a status tracker are.
bubbleMinSizeDpDp.UnspecifiedHow far across the smallest bubble of a scatter chart is: a point whose size is nothing, or too little to be seen as an area, is still a point.
bubbleMaxSizeDpDp.UnspecifiedHow far across its largest bubble is: the one whose size is the most of the chart's.
navigatorHeightDpDp.UnspecifiedHow tall the strip under a chart's plot is, on which the part of the chart that is in view is marked and moved, in a chart whose zoom has one: how wide, beside a plot whose places go down its side.
barBandShareFloatFloat.NaNHow much of a place's band its bars take, more than nought and one at the most: the rest is the room between one place's bars and the next's. Bars are no wider than barMaxWidth all the same.
markLineWidthDpDp.UnspecifiedThe width of a mark's line: a target, a limit, a day. Zero draws none, and the mark's words are written all the same.
markBandAlphaFloatFloat.NaNHow much of its colour a mark's band is drawn in, from nought to one: a band lies behind the series, and is a tint.
patternsBoolean?nullWhether the fills of a chart's series, slices and tiles are drawn with textures besides their colours, and its lines broken in turn (ChartPattern): what tells them apart for a reader who cannot tell the colours apart. Left out, as the system is set: on where it is set to tell things apart without colour, iOS's "Differentiate Without Color" and Android's colour correction, and off otherwise.
nodeWidthDpDp.UnspecifiedHow wide a node of a sankey is.
nodeGapDpDp.UnspecifiedBetween two nodes of a column of a sankey.

ChartColors

What a chart is painted with, for a chart that does not take the theme's colours: a colour that is set is used, and one that is left out stays the theme's.

ParameterTypeDefaultWhat it is
seriesList<Color>emptyList()The colours the series are drawn in, in the order they are handed out, for the series that name none of their own. Empty, they are the theme's chart colours.
gridColorColor.UnspecifiedThe lines drawn across the chart at the marks of an axis. The theme's divider.
axisColorColor.UnspecifiedThe line along an axis. The theme's border.
labelColorColor.UnspecifiedWhat is written beside the axes: the values, the places and the axes' titles, and the names in the legend. The theme's textSecondary.
crosshairColorColor.UnspecifiedThe line that marks the place pointed at. The theme's borderHover: stronger than the grid it crosses, and quieter than the series it stands behind.
tooltipContainerColorColor.UnspecifiedThe tooltip's fill. The theme's surface.
tooltipBorderColorColor.UnspecifiedThe tooltip's edge. The theme's border.
tooltipTextColorColor.UnspecifiedThe tooltip's heading and its values. The theme's textPrimary.
tooltipLabelColorColor.UnspecifiedThe names of the series in the tooltip. The theme's textSecondary.
risingColorColor.UnspecifiedWhat a value that goes up is drawn in: a step of a waterfall that adds. The theme's success.
fallingColorColor.UnspecifiedWhat a value that goes down is drawn in: a step of a waterfall that takes away. The theme's danger.

Accessibility