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
@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
@spec max_offset(non_neg_integer(), non_neg_integer()) :: non_neg_integer()
The scrollable range: how far the content extends past the viewport.
@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.
@spec thumb( non_neg_integer(), non_neg_integer(), non_neg_integer(), non_neg_integer() ) :: {non_neg_integer(), non_neg_integer()}
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.