# `GanttEx.Layout`
[🔗](https://github.com/rocket4ce/gantt_ex/blob/v0.1.0/lib/gantt_ex/layout.ex#L1)

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

# `bar`

```elixir
@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.

# `link_path`

```elixir
@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.

# `rollup`

```elixir
@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.

# `t`

```elixir
@type t() :: %{
  bars: [bar()],
  segments: [bar()],
  rollups: [rollup()],
  bar_index: %{required(GanttEx.Store.key()) =&gt; bar()},
  links: [link_path()],
  width: non_neg_integer(),
  height: non_neg_integer()
}
```

# `build`

```elixir
@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 to `36`.
  * `:bar_height` - defaults to `row_height - 12`.
  * `:min_bar_width` - defaults to `6`, so very short tasks stay visible.
  * `:baselines` - make room below the bars for the baselines
    (`:planned_start` / `:planned_end`). Defaults to `false`.

---

*Consult [api-reference.md](api-reference.md) for complete listing*
