GanttEx.Timeline (GanttEx v0.1.0)

Copy Markdown View Source

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

Types

A cell of a scale row or a column of the chart.

t()

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

cell()

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

t()

@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

cells(timeline, scale, formatter)

@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}]

column_count(timeline)

@spec column_count(t()) :: non_neg_integer()

Returns the number of columns.

columns(timeline)

@spec columns(t()) :: [cell()]

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}]

date_at(timeline, x)

@spec date_at(t(), number()) :: NaiveDateTime.t()

Returns the datetime at a horizontal position (the inverse of x/2).

fit(range, scales, opts \\ [])

@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]}

new(opts)

@spec new(keyword()) :: t()

Builds a timeline.

Options

  • :scales - list of scales (anything accepted by GanttEx.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 to 60.
  • :week_start - first ISO day of the week (1 = Monday). Defaults to 1.

unit_width(timeline, unit)

@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

width(timeline)

@spec width(t()) :: non_neg_integer()

Returns the total width in pixels.

x(timeline, datetime)

@spec x(t(), NaiveDateTime.t()) :: float()

Returns the horizontal position (in pixels) of a datetime. Dates outside the range are extrapolated.