New feature motivation
limel-chart can already draw everything a Gantt needs — an item whose value is a [start, end] pair renders as a span that begins away from zero, which is exactly what the limel-example-chart-type-gantt example shows. What it cannot do is label such a chart when the two numbers represent points in time, and time is the most natural thing to put on a Gantt.
The concrete case: we are adding configurable chart widgets to Lime CRM (see Lundalogik/hack-tuesday#361). An admin picks a filter set, a property to group records by, and one or two aggregates. That gives us Gantt-shaped data for free — "first and last activity date per project", "earliest and latest close date per pipeline stage", "shortest and longest handling time per ticket type". We had to leave all of them out, because there is no way to make the axis readable.
The problem
Two things combine:
ChartItem.value is number | [number, number]. A date has to be converted to a number by the consumer — epoch milliseconds is the only sensible choice.
renderAxisLines prints the raw number. It walks the range and emits <limel-badge label={value} /> with no formatting hook:
for (let value = minValue; value <= maxValue; value += increment) {
lines.push(
<div class={{ 'axis-line': true, 'zero-line': value === 0 }} role="presentation">
<limel-badge label={value} />
</div>
);
}
So a chart of dates gets axis badges reading 1735689600000, 1738368000000, … There is no prop to intercept them. ChartItem.formattedValue fixes the item label (the tooltip reads 2026-01-01 — 2026-02-01 if the consumer formats it), but the axis stays raw, and the axis is what makes a Gantt legible.
axisIncrement has the same problem from the other side: to get one tick per month a consumer has to pass a millisecond count, and months are not a fixed number of milliseconds, so the ticks drift.
New feature description
Some way for a consumer to control how axis values are rendered. A formatter callback is the smallest version:
@Prop() public formatAxisValue?: (value: number) => string;
…used in renderAxisLines in place of the bare label={value}. That alone unblocks the date case: the consumer converts to epoch, formats back with Intl.DateTimeFormat, and the chart stays agnostic about what the numbers mean.
A richer alternative would be first-class temporal support — letting value hold dates and having the chart choose sensible period ticks (day/week/month/quarter) itself. That is a much bigger change, and it would also solve the axisIncrement drift, but the formatter is enough to make date Gantts usable and does not commit the component to understanding calendars.
Worth noting a formatter prop would also help non-date cases that are awkward today: currency axes (1.2M rather than 1200000), percentages, and units.
New feature implementation
The change is contained to renderAxisLines plus one new @Prop. Both call sites already pass through a single place — renderAxisLines(range, direction?) is used for the single value axis and, twice, for scatter's two axes — so a per-axis variant would need a little more thought if x and y should ever format differently (a date-vs-count scatter would want exactly that).
Environment
- lime-elements version: 39.45.3 (also current on
main)
- Framework used: StencilJS, consumed via
@limetech/lime-crm-building-blocks
New feature motivation
limel-chartcan already draw everything a Gantt needs — an item whosevalueis a[start, end]pair renders as a span that begins away from zero, which is exactly what thelimel-example-chart-type-ganttexample shows. What it cannot do is label such a chart when the two numbers represent points in time, and time is the most natural thing to put on a Gantt.The concrete case: we are adding configurable chart widgets to Lime CRM (see Lundalogik/hack-tuesday#361). An admin picks a filter set, a property to group records by, and one or two aggregates. That gives us Gantt-shaped data for free — "first and last activity date per project", "earliest and latest close date per pipeline stage", "shortest and longest handling time per ticket type". We had to leave all of them out, because there is no way to make the axis readable.
The problem
Two things combine:
ChartItem.valueisnumber | [number, number]. A date has to be converted to a number by the consumer — epoch milliseconds is the only sensible choice.renderAxisLinesprints the raw number. It walks the range and emits<limel-badge label={value} />with no formatting hook:So a chart of dates gets axis badges reading
1735689600000,1738368000000, … There is no prop to intercept them.ChartItem.formattedValuefixes the item label (the tooltip reads2026-01-01 — 2026-02-01if the consumer formats it), but the axis stays raw, and the axis is what makes a Gantt legible.axisIncrementhas the same problem from the other side: to get one tick per month a consumer has to pass a millisecond count, and months are not a fixed number of milliseconds, so the ticks drift.New feature description
Some way for a consumer to control how axis values are rendered. A formatter callback is the smallest version:
…used in
renderAxisLinesin place of the barelabel={value}. That alone unblocks the date case: the consumer converts to epoch, formats back withIntl.DateTimeFormat, and the chart stays agnostic about what the numbers mean.A richer alternative would be first-class temporal support — letting
valuehold dates and having the chart choose sensible period ticks (day/week/month/quarter) itself. That is a much bigger change, and it would also solve theaxisIncrementdrift, but the formatter is enough to make date Gantts usable and does not commit the component to understanding calendars.Worth noting a formatter prop would also help non-date cases that are awkward today: currency axes (
1.2Mrather than1200000), percentages, and units.New feature implementation
The change is contained to
renderAxisLinesplus one new@Prop. Both call sites already pass through a single place —renderAxisLines(range, direction?)is used for the single value axis and, twice, forscatter's two axes — so a per-axis variant would need a little more thought if x and y should ever format differently (a date-vs-count scatter would want exactly that).Environment
main)@limetech/lime-crm-building-blocks