Packages
Multi-surface application runtime for Elixir. One TEA module renders to terminal, browser (LiveView), SSH, and MCP (agents). 30+ widgets, flexbox + CSS grid, AI agent runtime, distributed swarm with CRDTs, time-travel debugging, session recording, sandboxed REPL, and agentic commerce.
Current section
Files
Jump to
Current section
Files
lib/raxol/core/focus_manager.ex
defmodule Raxol.Core.FocusManager do
@moduledoc """
Refactored FocusManager that delegates to GenServer implementation.
This module provides the same API as the original FocusManager but uses
a supervised GenServer instead of the Process dictionary for state management.
## Migration Notice
This module is a drop-in replacement for `Raxol.Core.FocusManager`.
All functions maintain backward compatibility while providing improved
fault tolerance and functional programming patterns.
## Benefits over Process Dictionary
- Supervised state management with fault tolerance
- Pure functional transformations
- Better debugging and testing capabilities
- Clear separation of concerns
- No global state pollution
"""
@behaviour Raxol.Core.FocusManager.Behaviour
alias Raxol.Core.FocusManager.FocusServer, as: Server
@doc """
Ensures the FocusManager server is started.
Called automatically when using any function.
"""
def ensure_started do
case Process.whereis(Server) do
nil ->
{:ok, _pid} = Server.start_link()
:ok
_pid ->
:ok
end
end
@doc """
Register a focusable component with the focus manager.
"""
@impl Raxol.Core.FocusManager.Behaviour
def register_focusable(component_id, tab_index, opts \\ []) do
ensure_started()
Server.register_focusable(Server, component_id, tab_index, opts)
end
@doc """
Unregister a focusable component.
"""
@impl Raxol.Core.FocusManager.Behaviour
def unregister_focusable(component_id) do
ensure_started()
Server.unregister_focusable(Server, component_id)
end
@doc """
Set the initial focus to a specific component.
"""
@impl Raxol.Core.FocusManager.Behaviour
def set_initial_focus(component_id) do
ensure_started()
Server.set_initial_focus(Server, component_id)
end
@doc """
Set focus to a specific component.
"""
@impl Raxol.Core.FocusManager.Behaviour
def set_focus(component_id) do
ensure_started()
Server.set_focus(Server, component_id)
end
@doc """
Move focus to the next focusable element.
"""
@impl Raxol.Core.FocusManager.Behaviour
def focus_next(opts \\ []) do
ensure_started()
Server.focus_next(Server, opts)
end
@doc """
Move focus to the previous focusable element.
"""
@impl Raxol.Core.FocusManager.Behaviour
def focus_previous(opts \\ []) do
ensure_started()
Server.focus_previous(Server, opts)
end
@doc """
Get the ID of the currently focused element.
"""
@impl Raxol.Core.FocusManager.Behaviour
def get_focused_element do
ensure_started()
Server.get_focused_element(Server)
end
@doc """
Alias for get_focused_element/0.
"""
@impl Raxol.Core.FocusManager.Behaviour
def get_current_focus do
get_focused_element()
end
@doc """
Gets the focus history.
"""
@impl Raxol.Core.FocusManager.Behaviour
def get_focus_history do
ensure_started()
Server.get_focus_history(Server)
end
@doc """
Get the next focusable element after the given one.
"""
@impl Raxol.Core.FocusManager.Behaviour
def get_next_focusable(current_focus_id) do
ensure_started()
Server.get_next_focusable(Server, current_focus_id)
end
@doc """
Get the previous focusable element before the given one.
"""
@impl Raxol.Core.FocusManager.Behaviour
def get_previous_focusable(current_focus_id) do
ensure_started()
Server.get_previous_focusable(Server, current_focus_id)
end
@doc """
Check if a component has focus.
"""
@impl Raxol.Core.FocusManager.Behaviour
def has_focus?(component_id) do
ensure_started()
Server.has_focus?(Server, component_id)
end
@doc """
Return to the previously focused element.
"""
@impl Raxol.Core.FocusManager.Behaviour
def return_to_previous do
ensure_started()
Server.return_to_previous(Server)
end
@doc """
Enable a previously disabled focusable component.
"""
@impl Raxol.Core.FocusManager.Behaviour
def enable_component(component_id) do
ensure_started()
Server.enable_component(Server, component_id)
end
@doc """
Disable a focusable component, preventing it from receiving focus.
"""
@impl Raxol.Core.FocusManager.Behaviour
def disable_component(component_id) do
ensure_started()
Server.disable_component(Server, component_id)
end
@doc """
Register a handler function to be called when focus changes.
"""
@impl Raxol.Core.FocusManager.Behaviour
def register_focus_change_handler(handler_fun)
when is_function(handler_fun, 2) do
ensure_started()
Server.register_focus_change_handler(Server, handler_fun)
end
@doc """
Unregister a focus change handler function.
"""
@impl Raxol.Core.FocusManager.Behaviour
def unregister_focus_change_handler(handler_fun)
when is_function(handler_fun, 2) do
ensure_started()
Server.unregister_focus_change_handler(Server, handler_fun)
end
end