Maps time to pixels.
The timeline is divided into columns of the last (most detailed) scale.
Every column has the same width (:column_width), even when the underlying
periods do not (months have 28 to 31 days): positions are interpolated
inside each column, exactly like DHTMLX Gantt does.
iex> timeline = GanttEx.Timeline.new(
...> scales: [[unit: :month], [unit: :day]],
...> start_date: ~N[2025-01-01 00:00:00],
...> end_date: ~N[2025-02-01 00:00:00],
...> column_width: 40
...> )
iex> GanttEx.Timeline.column_count(timeline)
31
iex> GanttEx.Timeline.x(timeline, ~N[2025-01-03 12:00:00])
100.0
iex> GanttEx.Timeline.date_at(timeline, 100)
~N[2025-01-03 12:00:00]
iex> GanttEx.Timeline.width(timeline)
1240
Summary
Functions
Returns the cells of a scale row, clipped to the timeline range and
labelled with formatter (see GanttEx.I18n.formatter/1).
Returns the number of columns.
Returns the columns of the most detailed scale.
Returns the datetime at a horizontal position (the inverse of x/2).
Computes a comfortable visible range for the given date range.
Builds a timeline.
Returns how many pixels one unit spans, when it can be known (the unit
and the columns have a fixed length), or nil.
Returns the total width in pixels.
Returns the horizontal position (in pixels) of a datetime. Dates outside the range are extrapolated.
Types
@type cell() :: %{ start: NaiveDateTime.t(), end: NaiveDateTime.t(), x: float(), width: float(), label: String.t() | nil }
A cell of a scale row or a column of the chart.
@type t() :: %GanttEx.Timeline{ boundaries: tuple(), column_width: pos_integer(), end_date: NaiveDateTime.t(), scales: [GanttEx.Scale.t(), ...], start_date: NaiveDateTime.t(), week_start: 1..7 }
Functions
@spec cells(t(), GanttEx.Scale.t(), (NaiveDateTime.t(), term() -> String.t())) :: [ cell() ]
Returns the cells of a scale row, clipped to the timeline range and
labelled with formatter (see GanttEx.I18n.formatter/1).
iex> timeline = GanttEx.Timeline.new(scales: [[unit: :month], [unit: :day]], start_date: ~N[2025-01-30 00:00:00], end_date: ~N[2025-02-02 00:00:00], column_width: 10)
iex> [month, _day] = timeline.scales
iex> timeline |> GanttEx.Timeline.cells(month, GanttEx.I18n.formatter("en")) |> Enum.map(&{&1.label, &1.x, &1.width})
[{"Jan", 0.0, 20.0}, {"Feb", 20.0, 10.0}]
@spec column_count(t()) :: non_neg_integer()
Returns the number of columns.
Returns the columns of the most detailed scale.
iex> timeline = GanttEx.Timeline.new(scales: [[unit: :day]], start_date: ~N[2025-01-01 00:00:00], end_date: ~N[2025-01-03 00:00:00], column_width: 50)
iex> timeline |> GanttEx.Timeline.columns() |> Enum.map(&{&1.start.day, &1.x, &1.width})
[{1, 0, 50}, {2, 50, 50}]
@spec date_at(t(), number()) :: NaiveDateTime.t()
Returns the datetime at a horizontal position (the inverse of x/2).
@spec fit({NaiveDateTime.t(), NaiveDateTime.t()} | nil, [term()], keyword()) :: {NaiveDateTime.t(), NaiveDateTime.t()}
Computes a comfortable visible range for the given date range.
The range is padded by one column on each side and aligned to the first
(least detailed) scale, so the top header always shows complete periods.
It is then widened to at least :min_columns columns (defaults to 12).
When range is nil (no tasks) the range around :today is used.
iex> GanttEx.Timeline.fit({~N[2025-01-10 00:00:00], ~N[2025-01-20 00:00:00]}, [[unit: :month], [unit: :day]])
{~N[2025-01-01 00:00:00], ~N[2025-02-01 00:00:00]}
iex> GanttEx.Timeline.fit({~N[2025-01-01 00:00:00], ~N[2025-01-31 00:00:00]}, [[unit: :month], [unit: :day]])
{~N[2024-12-01 00:00:00], ~N[2025-02-01 00:00:00]}
Builds a timeline.
Options
:scales- list of scales (anything accepted byGanttEx.Scale.new/1). Required.:start_date,:end_date- the visible range. Required. The start is aligned to the most detailed scale.:column_width- width of a column in pixels. Defaults to60.:week_start- first ISO day of the week (1= Monday). Defaults to1.
@spec unit_width(t(), GanttEx.Dates.unit()) :: float() | nil
Returns how many pixels one unit spans, when it can be known (the unit
and the columns have a fixed length), or nil.
iex> timeline = GanttEx.Timeline.new(scales: [[unit: :week]], start_date: ~N[2025-01-06 00:00:00], end_date: ~N[2025-02-03 00:00:00], column_width: 70)
iex> GanttEx.Timeline.unit_width(timeline, :day)
10.0
@spec width(t()) :: non_neg_integer()
Returns the total width in pixels.
@spec x(t(), NaiveDateTime.t()) :: float()
Returns the horizontal position (in pixels) of a datetime. Dates outside the range are extrapolated.