Layout and ComposableChart
heat.layout builds v2 layoutConfiguration JSON for heat-system-next-dimension. Layout channels arrays must list the same string ids you used in heat.dataservice channel specs.
Two ways to supply layout:
| Approach | Where | Best for |
|---|---|---|
| Production | layoutConfiguration on the dimension node | Stable dashboards, template review |
| Iteration | suggestedLayoutConfiguration on arbex buildRoot() + dimension layoutFromParentOutput: true | Prototyping in script without editing dimension JSON |
Layout builder basics
const layout = heat.layout
.createBuilder({
version: "1.0.0",
realms: ["default"],
configuration: {
enableGlobalPlaybackControls: true,
enableLiveDataPolling: false,
defaultRealm: "default",
showRealmFilterDropdown: false,
},
})
.addRow()
.addColumn("BarChart", {
name: "Pods",
titleContent: "Pod counts",
channels: ["cluster-kpis"],
colspan: 6,
barChartItem: { /* widget-specific keys */ },
})
.build();| API | Why |
|---|---|
createBuilder(options?) | Page-level playback, realm filter, default realm |
addRow(opts?) | New grid row (category, tab, title, collapsible, …) |
addColumn(componentId, config) | Single-widget columns (BarChart, MapDisplay, StatsSummary, …) |
build() | Final layout object |
Each widget type uses a dedicated config key (barChartItem, mapDisplayItem, flightPathItem, …). See Next dashboard components for per-widget contracts.
ComposableChart (preferred for multi-lane charts)
Why ComposableChart: one column, shared time (or index) axis, multiple stacked slices (area, events lane, ranges lane, legend, static values), each bound to dataservice channel ids.
Fluent flow:
.addComposableChart({
name: "MainChart",
titleContent: "Metrics",
channels: ["synthetic-metric"], // must match ds channel id(s)
colspan: 12,
})
.anchor("time") // "time" | "index" | "none"
.xAxis({ timeLabelMode: "elapsed" }) // optional
.addAreaSlice({ label: "Series", height: 220, order: 1 })
.addSeries({
id: "line-1",
label: "Synthetic metric",
channel: "synthetic-metric",
color: "#38BDF8",
render: "line",
})
.yAxis({ domain: [0, 100] })
.endComposableChart()Slice helpers
| Method | Use for |
|---|---|
addAreaSlice | Line or step series (addSeries, yAxis) |
addScatterSlice | Scatter points |
addEventsLaneSlice | Boolean events (addEventSeries) |
addRangesLaneSlice | Interval bands |
addLegendSlice | Legend items (addLegendItem) |
addSpanningLinesSlice | Vertical markers (times([...])) |
addStaticSlice | Value channels (KPI-style) |
addPlaybackSlice() | Throws in production; use page-level enableGlobalPlaybackControls |
Return to the row builder with endComposableChart().
Ship layout with data from one run
const ds = heat.dataservice.createBuilder({ defaultRealm: "default" });
ds.addGroup("demo", "Demo");
ds.series({ id: "synthetic-metric", name: "Metric", groupId: "demo" }, points);
const layout = heat.layout.createBuilder({ version: "1.0.0", realms: ["default"] })
.addRow()
.addComposableChart({ name: "c", titleContent: "Chart", channels: ["synthetic-metric"], colspan: 12 })
.anchor("time")
.addAreaSlice({ height: 220, order: 1 })
.addSeries({ id: "s1", label: "Metric", channel: "synthetic-metric" })
.endComposableChart();
return ds.buildRoot({ suggestedLayoutConfiguration: layout });Enable on the arbex node:
{
"dataserviceOutput": {
"enabled": true,
"persistence": "dataservice-root",
"layoutOutput": { "enabled": true, "require": true }
}
}On heat-system-next-dimension: layoutFromParentOutput: true. Preset: sample-arbex-composable-dashboard. Script: sample-arbex-composable-dashboard.js.
Deprecated widgets
TimelineChart via addColumn("TimelineChart", …) is deprecated for new layouts. Prefer ComposableChart. See TimelineChart.