# cjm-fasthtml-sortable-queue


<!-- WARNING: THIS FILE WAS AUTOGENERATED! DO NOT EDIT! -->

## Install

``` bash
pip install cjm_fasthtml_sortable_queue
```

## Project Structure

    nbs/
    ├── config.ipynb      # Configuration dataclass for sortable queue instances.
    ├── handlers.ipynb    # Tier 1 handler functions for queue reorder, remove, and clear operations.
    ├── html_ids.ipynb    # HTML element ID generators for sortable queue components.
    ├── keyboard.ipynb    # Self-contained keyboard system for sortable queue navigation and actions.
    ├── models.ipynb      # URL bundle and reorder utility functions for sortable queues.
    ├── rendering.ipynb   # Queue panel and item rendering with callback-based custom content.
    ├── router.ipynb      # Tier 2 convenience router with auto-wired reorder, remove, and clear endpoints.
    └── sortable_js.ipynb # Sortable.js CDN loading and initialization script generation.

Total: 8 notebooks

## Module Dependencies

``` mermaid
graph LR
    config[config<br/>config]
    handlers[handlers<br/>handlers]
    html_ids[html_ids<br/>html_ids]
    keyboard[keyboard<br/>keyboard]
    models[models<br/>models]
    rendering[rendering<br/>rendering]
    router[router<br/>router]
    sortable_js[sortable_js<br/>sortable_js]

    handlers --> models
    handlers --> html_ids
    handlers --> config
    handlers --> rendering
    keyboard --> models
    keyboard --> html_ids
    keyboard --> config
    rendering --> config
    rendering --> models
    rendering --> html_ids
    router --> models
    router --> html_ids
    router --> handlers
    router --> config
```

*14 cross-module dependencies detected*

## CLI Reference

No CLI commands found in this project.

## Module Overview

Detailed documentation for each module in the project:

### config (`config.ipynb`)

> Configuration dataclass for sortable queue instances.

#### Import

``` python
from cjm_fasthtml_sortable_queue.config import (
    SortableQueueConfig,
    get_item_key
)
```

#### Functions

``` python
def get_item_key(
    config: SortableQueueConfig,  # Queue configuration
    item: dict,  # Queue item dict
) -> str:  # Unique key string for the item
    "Extract the unique key from a queue item using the config's key_fn or key_field."
```

#### Classes

``` python
@dataclass
class SortableQueueConfig:
    "Configuration for a sortable queue instance."
    
    prefix: str  # HTML ID prefix (e.g., "sq", "sd", "tss")
    key_field: str = 'id'  # Field name to extract item key (simple case)
    key_fn: Optional[Callable[[dict], str]]  # Custom key extraction (overrides key_field if set)
    item_class: str = 'queue-item'  # CSS class for queue items (keyboard selector + styling target)
    handle_class: str = 'drag-handle'  # CSS class for Sortable.js drag handle
    animation_ms: int = 150  # Sortable.js drag animation duration in milliseconds
    queue_title: str = 'Selected'  # Header title text
```

### handlers (`handlers.ipynb`)

> Tier 1 handler functions for queue reorder, remove, and clear
> operations.

#### Import

``` python
from cjm_fasthtml_sortable_queue.handlers import (
    handle_reorder,
    handle_reorder_by_direction,
    handle_remove,
    handle_clear
)
```

#### Functions

``` python
def handle_reorder(
    config: SortableQueueConfig,  # Queue configuration
    ids: SortableQueueHtmlIds,  # HTML ID generators
    urls: SortableQueueUrls,  # URL endpoints
    items: List[dict],  # Current item list
    new_key_order: List[str],  # Keys in new order (from form_data.getlist("item"))
    render_content: Callable[[dict, int], Any],  # Custom content callback
    **render_kwargs,  # Additional kwargs passed to render_sortable_queue
) -> tuple:  # (reordered_items, rendered_panel)
    "Reorder items by key order and return the updated list and rendered panel."
```

``` python
def handle_reorder_by_direction(
    config: SortableQueueConfig,  # Queue configuration
    ids: SortableQueueHtmlIds,  # HTML ID generators
    urls: SortableQueueUrls,  # URL endpoints
    items: List[dict],  # Current item list
    item_key: str,  # Key of item to move
    direction: str,  # "up" or "down"
    render_content: Callable[[dict, int], Any],  # Custom content callback
    **render_kwargs,  # Additional kwargs passed to render_sortable_queue
) -> tuple:  # (reordered_items, rendered_panel)
    "Move an item up or down and return the updated list and rendered panel."
```

``` python
def handle_remove(
    config: SortableQueueConfig,  # Queue configuration
    ids: SortableQueueHtmlIds,  # HTML ID generators
    urls: SortableQueueUrls,  # URL endpoints
    items: List[dict],  # Current item list
    remove_key: str,  # Key of item to remove
    render_content: Callable[[dict, int], Any],  # Custom content callback
    **render_kwargs,  # Additional kwargs passed to render_sortable_queue
) -> tuple:  # (updated_items, rendered_panel)
    "Remove an item by key and return the updated list and rendered panel."
```

``` python
def handle_clear(
    config: SortableQueueConfig,  # Queue configuration
    ids: SortableQueueHtmlIds,  # HTML ID generators
    urls: SortableQueueUrls,  # URL endpoints
    render_content: Callable[[dict, int], Any],  # Custom content callback
    **render_kwargs,  # Additional kwargs passed to render_sortable_queue
) -> tuple:  # (empty_list, rendered_panel)
    "Clear all items and return an empty list and rendered panel."
```

### html_ids (`html_ids.ipynb`)

> HTML element ID generators for sortable queue components.

#### Import

``` python
from cjm_fasthtml_sortable_queue.html_ids import (
    SortableQueueHtmlIds
)
```

#### Functions

``` python
def _safe_id(
    value: str,  # Raw value to sanitize
) -> str:  # HTML-safe ID fragment
    "Replace non-alphanumeric characters with hyphens for use in HTML IDs."
```

#### Classes

``` python
@dataclass
class SortableQueueHtmlIds:
    "HTML element ID generators for a sortable queue instance."
    
    prefix: str  # Instance prefix (e.g., "sq", "sd", "tss")
    
    def container(self) -> str:  # Outer container div ID
            """Queue panel container."""
            return f"{self.prefix}-queue-container"
    
        @property
        def list(self) -> str:  # Sortable <ul> ID
        "Queue panel container."
    
    def list(self) -> str:  # Sortable <ul> ID
            """Sortable queue list element."""
            return f"{self.prefix}-queue-list"
    
        @property
        def empty(self) -> str:  # Empty state div ID
        "Sortable queue list element."
    
    def empty(self) -> str:  # Empty state div ID
            """Empty state placeholder."""
            return f"{self.prefix}-queue-empty"
    
        @property
        def header(self) -> str:  # Header section ID
        "Empty state placeholder."
    
    def header(self) -> str:  # Header section ID
            """Queue header section."""
            return f"{self.prefix}-queue-header"
    
        @property
        def remove_btn(self) -> str:  # Hidden keyboard remove button ID
        "Queue header section."
    
    def remove_btn(self) -> str:  # Hidden keyboard remove button ID
            """Hidden button for keyboard-triggered remove."""
            return f"{self.prefix}-queue-remove-btn"
    
        @property
        def reorder_up_btn(self) -> str:  # Hidden keyboard reorder-up button ID
        "Hidden button for keyboard-triggered remove."
    
    def reorder_up_btn(self) -> str:  # Hidden keyboard reorder-up button ID
            """Hidden button for keyboard-triggered reorder up."""
            return f"{self.prefix}-queue-reorder-up-btn"
    
        @property
        def reorder_down_btn(self) -> str:  # Hidden keyboard reorder-down button ID
        "Hidden button for keyboard-triggered reorder up."
    
    def reorder_down_btn(self) -> str:  # Hidden keyboard reorder-down button ID
            """Hidden button for keyboard-triggered reorder down."""
            return f"{self.prefix}-queue-reorder-down-btn"
    
        @property
        def system_id(self) -> str:  # Keyboard system ID for hierarchy wiring
        "Hidden button for keyboard-triggered reorder down."
    
    def system_id(self) -> str:  # Keyboard system ID for hierarchy wiring
        "Keyboard system identifier for coordinator registration."
    
    def item(
        "Per-item HTML ID."
    
    def as_selector(
            self,
            html_id: str,  # An HTML ID string
        ) -> str:  # CSS selector string
        "Convert an HTML ID to a CSS selector."
```

### keyboard (`keyboard.ipynb`)

> Self-contained keyboard system for sortable queue navigation and
> actions.

#### Import

``` python
from cjm_fasthtml_sortable_queue.keyboard import (
    create_queue_keyboard_system
)
```

#### Functions

``` python
def create_queue_keyboard_system(
    config: SortableQueueConfig,  # Queue configuration
    ids: SortableQueueHtmlIds,  # HTML ID generators
    urls: SortableQueueUrls,  # URL endpoints for HTMX actions
    zone_focus_classes: tuple = (str(ring(0)),),  # CSS classes when queue zone is active
    item_focus_classes: Optional[tuple] = None,  # CSS classes on focused item (default: bg-base-300)
    data_attributes: tuple = (),  # Data attributes to extract (e.g., ("record-id", "provider-id"))
    on_focus_change: Optional[str] = None,  # JS callback on item focus change
    hidden_input_prefix: Optional[str] = None,  # Prefix for hidden state inputs
    system_id: Optional[str] = None,  # Keyboard system ID (auto-generated from ids.system_id if not set)
    show_hints: bool = False,  # Show keyboard hints UI
) -> KeyboardSystem:  # Complete rendered keyboard system
    """
    Create a self-contained keyboard system for the sortable queue.
    
    Returns a rendered KeyboardSystem with a single FocusZone (LinearVertical
    navigation) and built-in actions for Delete/Backspace remove and
    Shift+Arrow reorder. Works standalone or as a child in a hierarchy
    via `coord.setParent(system_id, parent_id)`.
    """
```

#### Variables

``` python
_DEFAULT_ITEM_FOCUS_CLASSES
```

### models (`models.ipynb`)

> URL bundle and reorder utility functions for sortable queues.

#### Import

``` python
from cjm_fasthtml_sortable_queue.models import (
    SortableQueueUrls,
    reorder_by_keys,
    reorder_by_direction
)
```

#### Functions

``` python
def reorder_by_keys(
    items: List[dict],  # Current item list
    new_key_order: List[str],  # Keys in desired order (from Sortable.js form data)
    key_fn: Callable[[dict], str],  # Function to extract key from item
) -> List[dict]:  # Reordered item list
    "Reorder items to match the key order from Sortable.js form data."
```

``` python
def reorder_by_direction(
    items: List[dict],  # Current item list
    item_key: str,  # Key of item to move
    direction: str,  # "up" or "down"
    key_fn: Callable[[dict], str],  # Function to extract key from item
) -> List[dict]:  # Reordered item list
    "Move an item up or down by swapping with its neighbor."
```

#### Classes

``` python
@dataclass
class SortableQueueUrls:
    "URL endpoints for sortable queue HTMX operations."
    
    reorder: str  # POST — Sortable.js drag-end reorder
    remove: str  # POST — Remove item from queue
    clear: str  # POST — Clear all items
```

### rendering (`rendering.ipynb`)

> Queue panel and item rendering with callback-based custom content.

#### Import

``` python
from cjm_fasthtml_sortable_queue.rendering import (
    render_queue_item,
    render_sortable_queue
)
```

#### Functions

``` python
def render_queue_item(
    config: SortableQueueConfig,  # Queue configuration
    ids: SortableQueueHtmlIds,  # HTML ID generators
    urls: SortableQueueUrls,  # URL endpoints
    item: dict,  # Queue item data
    index: int,  # 0-based position in queue
    render_content: Callable[[dict, int], Any],  # Callback for custom item content
    extra_attrs: Optional[dict] = None,  # Additional HTML attributes per item
) -> Any:  # Li element
    "Render a single queue item with drag handle, position, custom content, and remove button."
```

``` python
def _render_default_empty() -> Any:  # Empty state element
    "Default empty state when no items are in the queue."
```

``` python
def render_sortable_queue(
    config: SortableQueueConfig,  # Queue configuration
    ids: SortableQueueHtmlIds,  # HTML ID generators
    urls: SortableQueueUrls,  # URL endpoints
    queue_items: List[dict],  # Ordered list of queue item dicts
    render_content: Callable[[dict, int], Any],  # Callback for custom item content
    render_empty: Optional[Callable[[], Any]] = None,  # Custom empty state (default provided)
    render_header_actions: Optional[Callable[[List[dict], SortableQueueUrls], Any]] = None,  # Custom header actions
    render_footer: Optional[Callable[[List[dict]], Any]] = None,  # Optional footer content
    extra_item_attrs: Optional[Callable[[dict], dict]] = None,  # Additional data-* attributes per item
    container_classes: tuple = (),  # Additional classes on container div
) -> Any:  # Queue panel element
    "Render the complete sortable queue panel."
```

### router (`router.ipynb`)

> Tier 2 convenience router with auto-wired reorder, remove, and clear
> endpoints.

#### Import

``` python
from cjm_fasthtml_sortable_queue.router import (
    init_sortable_queue_router
)
```

#### Functions

``` python
def init_sortable_queue_router(
    config: SortableQueueConfig,  # Queue configuration
    get_items: Callable[[str], List[dict]],  # (session_id) -> current items
    set_items: Callable[[str, List[dict]], None],  # (session_id, items) -> persist
    render_content: Callable[[dict, int], Any],  # Custom content callback
    on_mutate: Optional[Callable[[str, List[dict], str], tuple]] = None,  # (mutation_type, items, sess) -> OOB elements
    prefix: str = "/queue",  # Route prefix
    **render_kwargs,  # Additional kwargs passed to render_sortable_queue
) -> Tuple[APIRouter, SortableQueueUrls]:  # (router, urls) tuple
    """
    Initialize a router with reorder, remove, and clear endpoints.
    
    The `on_mutate` callback receives the mutation type ("reorder", "remove",
    "clear"), the updated items list, and the session ID. It should return a
    tuple of OOB elements to append to the response, or an empty tuple.
    """
```

### sortable_js (`sortable_js.ipynb`)

> Sortable.js CDN loading and initialization script generation.

#### Import

``` python
from cjm_fasthtml_sortable_queue.sortable_js import (
    SORTABLE_JS_CDN,
    sortable_js_headers,
    generate_sortable_init_script
)
```

#### Functions

``` python
def sortable_js_headers() -> tuple:  # Tuple of Script elements for app headers
    "CDN script tag for Sortable.js, suitable for FastHTML app headers."
```

``` python
def generate_sortable_init_script(
    handle_class: str = "drag-handle",  # CSS class for drag handle elements
    animation_ms: int = 150,  # Drag animation duration in milliseconds
) -> Script:  # Script element with Sortable.js initialization
    """
    Generate htmx.onLoad script that initializes Sortable.js on `.sortable` elements.
    
    Includes the disable-on-drag/re-enable-on-swap pattern to prevent
    double-firing during HTMX response processing.
    """
```

#### Variables

``` python
SORTABLE_JS_CDN = 'https://cdn.jsdelivr.net/npm/sortablejs@1.15.0/Sortable.min.js'
```
