Tuix.Components.ScrollBox (tuix v0.1.4)

Copy Markdown View Source

The behaviour implementation and pure helpers behind Tuix.Components.scroll_box/2, a focusable, vertically scrolling container.

Scroll boxes are built on the focus model: focus one (Tab traversal or autofocus) and :up / :down scroll by a row, :page_up / :page_down by a viewport, and :home / :end jump to the boundaries. The offset is ephemeral framework state (like an input's cursor): no event reaches the app and there is nothing to assign back. Scrolling is clamped (boundary presses are consumed); everything else falls through to the app with target set.

The stored state is %{offset: n, viewport: h, max_offset: m} — the runtime refreshes viewport and max_offset from each layout (see Tuix.Runtime), so on_key/3 can page and clamp without recomputing layout. With snap: :bottom, a box whose offset sits at max_offset is pinned: it stays at the bottom as content grows.

Summary

Types

The framework-managed scroll state: the current offset (rows scrolled past the top), the viewport height, and the scrollable range — the latter two measured by the last layout.

Functions

The scrollable range: how far the content extends past the viewport.

Resolves the paint-time scroll offset from the marked props: the framework-injected :scroll_offset (an integer, or :bottom for a box pinned by snap: :bottom) clamped to the scrollable range. Without a stored offset, snap: :bottom boxes start at the bottom.

Scrollbar thumb geometry: {row, height} within a track_height-row track. The thumb height is the visible share of the content (at least one row), and its row is proportional to the offset — exactly at the end of the track when fully scrolled.

Types

state()

@type state() :: %{
  offset: non_neg_integer(),
  viewport: non_neg_integer(),
  max_offset: non_neg_integer()
}

The framework-managed scroll state: the current offset (rows scrolled past the top), the viewport height, and the scrollable range — the latter two measured by the last layout.

Functions

max_offset(content_height, viewport_height)

@spec max_offset(non_neg_integer(), non_neg_integer()) :: non_neg_integer()

The scrollable range: how far the content extends past the viewport.

resolve_offset(props, content_height, viewport_height)

@spec resolve_offset(map(), non_neg_integer(), non_neg_integer()) :: non_neg_integer()

Resolves the paint-time scroll offset from the marked props: the framework-injected :scroll_offset (an integer, or :bottom for a box pinned by snap: :bottom) clamped to the scrollable range. Without a stored offset, snap: :bottom boxes start at the bottom.

thumb(offset, content_height, viewport_height, track_height)

Scrollbar thumb geometry: {row, height} within a track_height-row track. The thumb height is the visible share of the content (at least one row), and its row is proportional to the offset — exactly at the end of the track when fully scrolled.