Hidden Inputs

Generate hidden inputs for HTMX integration with keyboard navigation.

Zone Data Inputs

Generate hidden inputs for tracking focused item data attributes.

The name attribute converts hyphens to underscores for Python/FastHTML compatibility: - HTML data attribute: data-item-id - Hidden input name: item_id - FastHTML route parameter: item_id


source

render_zone_hidden_inputs


def render_zone_hidden_inputs(
    zone:FocusZone, # the focus zone configuration
)->list: # list of Hidden input components

Render hidden inputs for a single zone’s data attributes.

# Test zone inputs
from fasthtml.common import to_xml

zone = FocusZone(
    id="browser",
    item_selector="tr",
    data_attributes=("job-id", "plugin-name")
)

inputs = render_zone_hidden_inputs(zone)
assert len(inputs) == 2

html = to_xml(inputs[0])
assert 'id="browser-job-id"' in html
# Name converts hyphens to underscores for Python compatibility
assert 'name="job_id"' in html

Manager Hidden Inputs

Generate all hidden inputs for a ZoneManager.


source

render_hidden_inputs


def render_hidden_inputs(
    manager:ZoneManager, # the zone manager configuration
    include_state:bool=False, # include state tracking inputs
    container_id:str='kb-hidden-inputs', # container element ID
)->Div: # container with all hidden inputs

Render all hidden inputs for keyboard navigation.

Deduplicates inputs by ID - zones with the same hidden_input_prefix will share inputs rather than creating duplicates.

# Test manager inputs
from cjm_fasthtml_keyboard_navigation.core.manager import ZoneManager

zone1 = FocusZone(id="z1", data_attributes=("a",))
zone2 = FocusZone(id="z2", data_attributes=("b", "c"))

manager = ZoneManager(zones=(zone1, zone2))
container = render_hidden_inputs(manager)

html = to_xml(container)
assert 'id="kb-hidden-inputs"' in html
assert 'id="z1-a"' in html
assert 'id="z2-b"' in html
assert 'id="z2-c"' in html
# Test deduplication - zones with same prefix share inputs
zone_browser = FocusZone(
    id="browser",
    item_selector="tr",
    data_attributes=("job-id", "plugin-name"),
    hidden_input_prefix="sd-focused"
)
zone_queue = FocusZone(
    id="queue",
    item_selector="li",
    data_attributes=("job-id", "plugin-name"),
    hidden_input_prefix="sd-focused"  # Same prefix as browser
)

manager_shared = ZoneManager(zones=(zone_browser, zone_queue))
container = render_hidden_inputs(manager_shared)

html = to_xml(container)
# Should only have 2 inputs (deduplicated), not 4
assert html.count('id="sd-focused-job-id"') == 1
assert html.count('id="sd-focused-plugin-name"') == 1
assert html.count('<input') == 2
# Test with state inputs
container = render_hidden_inputs(manager, include_state=True)
html = to_xml(container)
assert 'id="kb-active-zone"' in html
assert 'id="kb-current-mode"' in html

Include Selector Builder

Build hx-include selector for HTMX requests.


source

build_include_selector


def build_include_selector(
    zone:FocusZone, # the zone to include inputs from
    include_state:bool=False, # include state inputs
)->str: # CSS selector for hx-include

Build hx-include selector for zone’s hidden inputs.

# Test include selector
zone = FocusZone(
    id="browser",
    data_attributes=("job-id", "plugin-name")
)

selector = build_include_selector(zone)
assert selector == "#browser-job-id, #browser-plugin-name"

selector_with_state = build_include_selector(zone, include_state=True)
assert "#kb-active-zone" in selector_with_state

source

build_all_zones_include_selector


def build_all_zones_include_selector(
    manager:ZoneManager, # the zone manager
    include_state:bool=False, # include state inputs
)->str: # CSS selector for all zones

Build hx-include selector for all zones’ hidden inputs.

Deduplicates selectors - zones with the same hidden_input_prefix will only include each input once.

# Test all zones selector
selector = build_all_zones_include_selector(manager)
assert "#z1-a" in selector
assert "#z2-b" in selector
assert "#z2-c" in selector
# Test deduplication in selector - zones with same prefix
selector = build_all_zones_include_selector(manager_shared)
# Should only have 2 selectors (deduplicated), not 4
assert selector.count("#sd-focused-job-id") == 1
assert selector.count("#sd-focused-plugin-name") == 1
assert selector == "#sd-focused-job-id, #sd-focused-plugin-name"