Computes the geometry of the chart: one bar per visible row and one path per visible link.
Tasks rendered with render: :split draw their children as segments on
their own row (and links reach the segments while the branch is collapsed),
and tasks with rollup: true also appear as rollups on the rows of
their ancestor projects.
The layout is pure data, so it can be tested without rendering anything:
iex> store = GanttEx.Store.new!(
...> [
...> %{id: 1, text: "A", start_date: ~D[2025-01-01], duration: 2},
...> %{id: 2, text: "B", start_date: ~D[2025-01-04], type: :milestone}
...> ],
...> [%{id: 1, source: 1, target: 2}]
...> )
iex> timeline = GanttEx.Timeline.new(scales: [[unit: :day]], start_date: ~N[2025-01-01 00:00:00], end_date: ~N[2025-01-10 00:00:00], column_width: 40)
iex> layout = GanttEx.Layout.build(store, GanttEx.Store.rows(store), timeline, row_height: 36, bar_height: 24)
iex> [a, b] = layout.bars
iex> {a.x, a.width, a.y, a.kind}
{0.0, 80.0, 6.0, :task}
iex> {b.x, b.width, b.kind}
{108.0, 24, :milestone}
iex> [link] = layout.links
iex> link.d
"M80 18L92 18L92 54L102 54"
iex> layout.height
72
Summary
Functions
Builds the layout for the given rows (see GanttEx.Store.rows/2).
Types
@type bar() :: %{ key: GanttEx.Store.key(), task: GanttEx.Task.t(), row: non_neg_integer(), kind: GanttEx.Task.type(), x: float(), y: float(), width: float(), height: number(), progress: float(), baseline: %{x: float(), y: float(), width: float(), milestone: boolean()} | nil, uid: String.t(), segment: boolean() }
A positioned task bar.
@type link_path() :: %{ key: GanttEx.Store.key(), link: GanttEx.Link.t(), source_key: GanttEx.Store.key(), target_key: GanttEx.Store.key(), source_row: non_neg_integer(), target_row: non_neg_integer(), d: String.t(), arrow_d: String.t() }
A positioned link.
@type rollup() :: %{ uid: String.t(), task: GanttEx.Task.t(), row: non_neg_integer(), kind: GanttEx.Task.type(), x: float(), y: float(), width: float() }
A task shown on the row of an ancestor project.
@type t() :: %{ bars: [bar()], segments: [bar()], rollups: [rollup()], bar_index: %{required(GanttEx.Store.key()) => bar()}, links: [link_path()], width: non_neg_integer(), height: non_neg_integer() }
Functions
@spec build(GanttEx.Store.t(), [GanttEx.Store.row()], GanttEx.Timeline.t(), keyword()) :: t()
Builds the layout for the given rows (see GanttEx.Store.rows/2).
Options
:row_height- defaults to36.:bar_height- defaults torow_height - 12.:min_bar_width- defaults to6, so very short tasks stay visible.:baselines- make room below the bars for the baselines (:planned_start/:planned_end). Defaults tofalse.