Behaviour implemented by stateful renderer extensions.
An implicit owns state for an element without requiring the parent view to
handle every interaction itself. It must implement init/3 and
handle_modifiers/3. Event handling and animation are optional.
Initialization
children is a list of attribute maps for elements owned by the implicit.
root_attrs is the implicit root's attribute map with :id included.
last_state is the state from the preceding render; once layout is known it
also contains the previous Breeze.Viewport under :__element__.
Implicit modules must be referenced statically in compiled templates. Initialization may run
during input-routing reconciliation as well as while a render settles state and layout, so
init/3 must not rely on a particular call count and should not perform side effects.
An initializer returns {:ok, state} or {:ok, state, options}. The options are:
:rerender_every- a positive interval in milliseconds for asynchronousanimate/5calls. It has no effect unlessanimate/5is implemented.:active_when_pending- run periodic animation only while an input event is pending. If this and:active_when_focusedare both true, pending state takes precedence.:active_when_focused- run periodic animation only while the implicit root is focused.:captures_keys-trueto capture every key, or a list of key names to capture selectively.:captures_printable_keys- capture printable input and supported text editing keys.:batch_printable_keys- deliver adjacent printable input as one event whose"key"contains the combined text and whose"__batched_printable__"flag is true. This is opt-in and only applies with:captures_printable_keys.:captures_control_keys- capture control-modified key input.:captures_focus_keys- capture Tab and Shift-Tab input.:requires_layout_rerender- rerender after the implicit's dimensions change so layout-dependent state can settle.:state_change_requires_rerender- set tofalsewhen changing only the implicit state does not require another render. The default istrue.
Events
handle_event/3 receives a reserved event-kind argument, the event payload,
and the current implicit state. Input payloads use string keys. Breeze adds
the target's Breeze.Viewport to the payload as "element" when one is
available.
Every reply stores the returned state. {:noreply, state} stops there.
{{:change, event}, state} and {{:submit, event}, state} route event through
the root element's br-change or br-submit handler. The event payload may
be any term; Breeze's built-in implicits currently use atom-keyed maps. A
change reply may also contain focus: id (or focus: nil) as its third
element. Finally, {{:delegate, id}, state} sends the original input event to
another implicit.
Render modifiers
handle_modifiers/3 runs for the implicit root and each owned child during
rendering. Its first argument is :root or :child; its second is the
element's attribute keyword list, with :layout_element added when a previous
Breeze.Viewport is available.
It returns a keyword list. style: value adds a binary, map, or list of style
modifiers. scroll_y: top, scroll_x: left, and
scroll: {top, left} control the viewport offset. Any other atom/value pair
becomes a renderer flag; common examples include selected: true,
default_focus: true, and focus_scope: :trap.
Animation
animate/5 receives the element type, its rendered BackBreeze.Box, the
element flags, implicit state, and a context map. It may return a box directly,
{:ok, box}, or {:ok, box, options}. The only animation option is
overlays: overlays; overlays are applied during asynchronous animation
passes.
Breeze calls animate/5 for roots and children during a normal render.
:rerender_every additionally schedules lightweight asynchronous calls for
the root. The context contains :phase, :frame, :now, :pending?,
:focused?, :last_render_at, :last_interaction_at, :theme, :id, and
:layout.
Summary
Types
Runtime context supplied to animate/5.
An option returned from an animation pass.
A valid return value from animate/5.
Attributes collected from an implicit element.
The implicit root or one of its child elements.
An option returned with an implicit event action.
An unrestricted named-event payload emitted by an implicit.
A valid return value from handle_event/3.
The kind of input event dispatched to an implicit.
Renderer flags attached to an implicit element.
An option returned while initializing implicit state.
A valid return value from init/3.
A string-keyed terminal input payload supplied to an implicit.
A decoded terminal key name.
A style, scroll, or state modifier returned during rendering.
State map owned by an implicit implementation.
Callbacks
Transforms a rendered box during the normal render or a periodic animation pass.
Handles an event captured by the implicit and returns its next state and action.
Returns renderer modifiers for the implicit root or one of its children.
Initializes state from the owned children, root attributes, and previous state.
Types
@type animation_context() :: %{ phase: :base | :async, frame: non_neg_integer(), now: integer() | nil, pending?: boolean(), focused?: boolean(), last_render_at: integer() | nil, last_interaction_at: integer() | nil, theme: Breeze.Theme.t(), id: String.t() | nil, layout: Breeze.Viewport.t() | nil }
Runtime context supplied to animate/5.
@type animation_option() :: {:overlays, [map()]}
An option returned from an animation pass.
@type animation_reply() :: %BackBreeze.Box{ bottom: term(), children: term(), content: term(), display: term(), fixed_layer_map: term(), height: term(), layer: term(), layer_map: term(), left: term(), overlay?: term(), position: term(), right: term(), scroll: term(), state: term(), style: term(), top: term(), width: term() } | {:ok, %BackBreeze.Box{ bottom: term(), children: term(), content: term(), display: term(), fixed_layer_map: term(), height: term(), layer: term(), layer_map: term(), left: term(), overlay?: term(), position: term(), right: term(), scroll: term(), state: term(), style: term(), top: term(), width: term() }} | {:ok, %BackBreeze.Box{ bottom: term(), children: term(), content: term(), display: term(), fixed_layer_map: term(), height: term(), layer: term(), layer_map: term(), left: term(), overlay?: term(), position: term(), right: term(), scroll: term(), state: term(), style: term(), top: term(), width: term() }, [animation_option()]}
A valid return value from animate/5.
@type attributes() :: map()
Attributes collected from an implicit element.
@type element_type() :: :root | :child
The implicit root or one of its child elements.
@type event_option() :: {:focus, String.t() | nil}
An option returned with an implicit event action.
@type event_payload() :: term()
An unrestricted named-event payload emitted by an implicit.
@type event_reply() :: {:noreply, state()} | {{:change, event_payload()}, state()} | {{:change, event_payload()}, state(), [event_option()]} | {{:submit, event_payload()}, state()} | {{:delegate, String.t()}, state()}
A valid return value from handle_event/3.
@type event_type() :: :input
The kind of input event dispatched to an implicit.
@type flags() :: keyword()
Renderer flags attached to an implicit element.
@type init_option() :: {:rerender_every, pos_integer()} | {:active_when_pending, boolean()} | {:active_when_focused, boolean()} | {:captures_keys, boolean() | [key_name()]} | {:captures_printable_keys, boolean()} | {:batch_printable_keys, boolean()} | {:captures_control_keys, boolean()} | {:captures_focus_keys, boolean()} | {:requires_layout_rerender, boolean()} | {:state_change_requires_rerender, boolean()}
An option returned while initializing implicit state.
@type init_result() :: {:ok, state()} | {:ok, state(), [init_option()]}
A valid return value from init/3.
A string-keyed terminal input payload supplied to an implicit.
@type key_name() :: String.t()
A decoded terminal key name.
@type modifier() :: {:style, binary() | map() | list()} | {:scroll_y, integer()} | {:scroll_x, integer()} | {:scroll, {integer(), integer()}} | {atom(), term()}
A style, scroll, or state modifier returned during rendering.
@type state() :: map()
State map owned by an implicit implementation.
Callbacks
@callback animate( element_type(), %BackBreeze.Box{ bottom: term(), children: term(), content: term(), display: term(), fixed_layer_map: term(), height: term(), layer: term(), layer_map: term(), left: term(), overlay?: term(), position: term(), right: term(), scroll: term(), state: term(), style: term(), top: term(), width: term() }, flags(), state(), animation_context() ) :: animation_reply()
Transforms a rendered box during the normal render or a periodic animation pass.
@callback handle_event(event_type(), input_event(), state()) :: event_reply()
Handles an event captured by the implicit and returns its next state and action.
@callback handle_modifiers(element_type(), flags(), state()) :: [modifier()]
Returns renderer modifiers for the implicit root or one of its children.
@callback init([attributes()], attributes(), state()) :: init_result()
Initializes state from the owned children, root attributes, and previous state.