Keyboard System

High-level API for rendering complete keyboard navigation systems.

KeyboardSystem Result


source

KeyboardSystem


def KeyboardSystem(
    script:Script, hidden_inputs:Div, action_buttons:Div, hints:Optional[Div]=None
)->None:

Container for all keyboard navigation components.

Render Keyboard System

The main entry point for generating keyboard navigation components.

If include_map is not provided, it will be auto-generated based on the actions: - Actions with zone_ids will include only those zones’ hidden inputs - Actions without zone_ids will include all zones’ hidden inputs

This ensures HTMX requests automatically include the focused item’s data attributes.


source

render_keyboard_system


def render_keyboard_system(
    manager:ZoneManager, # the zone manager configuration
    url_map:dict[str, str], # action button ID -> URL
    target_map:dict[str, str], # action button ID -> target selector
    include_map:dict[str, str] | None=None, # action button ID -> include selector (auto-generated if None)
    swap_map:dict[str, str] | None=None, # action button ID -> swap value
    vals_map:dict[str, dict] | None=None, # action button ID -> hx-vals dict
    show_hints:bool=True, # render keyboard hints UI
    hints_badge_style:str='ghost', # badge style for hints
    include_state_inputs:bool=False, # include state tracking inputs
)->KeyboardSystem: # complete keyboard system

Render complete keyboard navigation system.

# Test render_keyboard_system
from fasthtml.common import to_xml
from cjm_fasthtml_keyboard_navigation.core.focus_zone import FocusZone
from cjm_fasthtml_keyboard_navigation.core.actions import KeyAction

browser = FocusZone(
    id="browser",
    item_selector="tr.item",
    data_attributes=("job-id",)
)
queue = FocusZone(
    id="queue",
    item_selector="li.item"
)

manager = ZoneManager(
    zones=(browser, queue),
    actions=(
        KeyAction(key=" ", htmx_trigger="toggle-btn", description="Select"),
        KeyAction(key="Delete", htmx_trigger="delete-btn", description="Remove"),
    )
)

system = render_keyboard_system(
    manager,
    url_map={
        "toggle-btn": "/toggle",
        "delete-btn": "/delete"
    },
    target_map={
        "toggle-btn": "#list",
        "delete-btn": "#list"
    }
)

assert isinstance(system, KeyboardSystem)
assert system.hints is not None

# Check script
script_html = to_xml(system.script)
assert "handleKeydown" in script_html

# Check hidden inputs
inputs_html = to_xml(system.hidden_inputs)
assert 'id="browser-job-id"' in inputs_html

# Check action buttons - no hx-trigger by default (JS handles triggering)
buttons_html = to_xml(system.action_buttons)
assert 'id="toggle-btn"' in buttons_html
assert 'id="delete-btn"' in buttons_html
assert 'hx-trigger' not in buttons_html  # JavaScript handles triggering

# Check hints
hints_html = to_xml(system.hints)
assert "Select" in hints_html
# Test without hints
system_no_hints = render_keyboard_system(
    manager,
    url_map={"toggle-btn": "/toggle", "delete-btn": "/delete"},
    target_map={"toggle-btn": "#list", "delete-btn": "#list"},
    show_hints=False
)

assert system_no_hints.hints is None
assert len(system_no_hints.all_components()) == 3  # No hints
# Test with vals_map for direction parameters
manager_with_reorder = ZoneManager(
    zones=(browser, queue),
    actions=(
        KeyAction(key="ArrowUp", modifiers=frozenset({"shift"}), htmx_trigger="reorder-up-btn"),
        KeyAction(key="ArrowDown", modifiers=frozenset({"shift"}), htmx_trigger="reorder-down-btn"),
    )
)

system_with_vals = render_keyboard_system(
    manager_with_reorder,
    url_map={
        "reorder-up-btn": "/reorder",
        "reorder-down-btn": "/reorder"
    },
    target_map={
        "reorder-up-btn": "#queue",
        "reorder-down-btn": "#queue"
    },
    vals_map={
        "reorder-up-btn": {"direction": "up"},
        "reorder-down-btn": {"direction": "down"}
    },
    show_hints=False
)

buttons_html = to_xml(system_with_vals.action_buttons)
assert '"direction": "up"' in buttons_html
assert '"direction": "down"' in buttons_html
# Test all_components unpacking
components = system.all_components()
assert len(components) == 4  # script, inputs, buttons, hints

Quick Setup Function

For simpler use cases with sensible defaults.


source

quick_keyboard_system


def quick_keyboard_system(
    zones:tuple[FocusZone, ...], # focus zones
    actions:tuple[KeyAction, ...], # keyboard actions
    url_map:dict[str, str], # action URLs
    target_map:dict[str, str], # action targets
    kwargs:VAR_KEYWORD
)->KeyboardSystem: # complete keyboard system

Quick setup for simple keyboard navigation.

# Test quick setup
system = quick_keyboard_system(
    zones=(browser,),
    actions=(
        KeyAction(key=" ", htmx_trigger="select-btn", description="Select"),
    ),
    url_map={"select-btn": "/select"},
    target_map={"select-btn": "#list"},
    show_hints=False
)

assert isinstance(system, KeyboardSystem)
assert system.hints is None