# Mouse Support - Documentation ## Overview The mouse support library provides keyboard-like binding capabilities for mouse actions. It supports simple clicks, modified clicks (with Ctrl/Shift/Alt), and sequences of clicks with smart timeout logic. ## Features ### Supported Mouse Actions **Basic Actions**: - `click` - Left click - `right_click` (or `rclick`) - Right click (contextmenu) **Modified Actions**: - `ctrl+click` (or `ctrl+rclick`) - Ctrl+Click (or Cmd+Click on Mac) - `shift+click` (or `shift+rclick`) - Shift+Click - `alt+click` (or `alt+rclick`) - Alt+Click - `ctrl+shift+click` - Multiple modifiers - Any combination of modifiers **Sequences**: - `click right_click` (or `click rclick`) - Click then right-click within 500ms - `click click` - Double click sequence - `ctrl+click click` - Ctrl+click then normal click - Any sequence of actions **Note**: `rclick` is an alias for `right_click` and can be used interchangeably. ### Smart Timeout Logic Same as keyboard support: - If **any element** has a longer sequence possible, **all matching elements wait** - Timeout is 500ms between actions - Immediate trigger if no longer sequences exist ### Multiple Element Support Multiple elements can listen to the same mouse action and all will trigger simultaneously. ## Configuration Format Uses HTMX configuration objects (same as keyboard support): ```javascript const combinations = { "click": { "hx-post": "/handle-click", "hx-target": "#result" }, "ctrl+click": { "hx-post": "/handle-ctrl-click", "hx-swap": "innerHTML" }, "rclick": { // Alias for right_click "hx-post": "/context-menu" }, "click rclick": { // Can use rclick in sequences too "hx-post": "/sequence-action", "hx-vals": {"type": "sequence"} } }; add_mouse_support('my-element', JSON.stringify(combinations)); ``` ### Dynamic Values with JavaScript Functions You can add dynamic values computed at click time using `hx-vals-extra`. This is useful when combined with a Command (which provides `hx-vals` with `c_id`). **Configuration format:** ```javascript const combinations = { "click": { "hx-post": "/myfasthtml/commands", "hx-vals": {"c_id": "command_id"}, // Static values from Command "hx-vals-extra": {"js": "getClickData"} // Dynamic values via JS function } }; ``` **How it works:** 1. `hx-vals` contains static values (e.g., `c_id` from Command) 2. `hx-vals-extra.dict` contains additional static values (merged) 3. `hx-vals-extra.js` specifies a function to call for dynamic values (merged) **JavaScript function definition:** ```javascript // Function receives (event, element, combinationStr) function getClickData(event, element, combination) { return { x: event.clientX, y: event.clientY, target_id: event.target.id, timestamp: Date.now() }; } ``` The function parameters are optional - use what you need: ```javascript // Full context function getFullContext(event, element, combination) { return { x: event.clientX, elem: element.id, combo: combination }; } // Just the event function getPosition(event) { return { x: event.clientX, y: event.clientY }; } // No parameters needed function getTimestamp() { return { ts: Date.now() }; } ``` **Built-in helper function:** ```javascript // getCellId() - finds parent with .dt2-cell class and returns its id function getCellId(event) { const cell = event.target.closest('.dt2-cell'); if (cell && cell.id) { return { cell_id: cell.id }; } return {}; } ``` ## API Reference ### add_mouse_support(elementId, combinationsJson) Adds mouse support to an element. **Parameters**: - `elementId` (string): ID of the HTML element - `combinationsJson` (string): JSON string of combinations with HTMX configs **Returns**: void **Example**: ```javascript add_mouse_support('button1', JSON.stringify({ "click": {"hx-post": "/click"}, "ctrl+click": {"hx-post": "/ctrl-click"} })); ``` ### remove_mouse_support(elementId) Removes mouse support from an element. **Parameters**: - `elementId` (string): ID of the HTML element **Returns**: void **Side effects**: - If last element: detaches global event listeners and cleans up all state **Example**: ```javascript remove_mouse_support('button1'); ``` ## Automatic Parameters The library automatically adds these parameters to every HTMX request: - `combination` - The mouse combination that triggered the action (e.g., "ctrl+click") - `has_focus` - Boolean indicating if the element had focus when clicked - `is_inside` - Boolean indicating if the click was inside the element - For `click`: `true` if clicked inside element, `false` if clicked outside - For `right_click`: always `true` (only triggers when clicking on element) - `has_focus` - Boolean indicating if the element had focus when the action triggered - `clicked_inside` - Boolean indicating if the click was inside the element or outside ### Parameter Details **`has_focus`**: - `true` if the registered element currently has focus - `false` otherwise - Useful for knowing if the element was the active element **`clicked_inside`**: - For `click` actions: `true` if clicked on/inside the element, `false` if clicked outside - For `right_click` actions: always `true` (since right-click only triggers on the element) - Useful for "click outside to close" logic **Example values sent**: ```javascript // User clicks inside a modal { combination: "click", has_focus: true, clicked_inside: true } // User clicks outside the modal (modal still gets triggered because click is global) { combination: "click", has_focus: false, clicked_inside: false // Perfect for closing the modal! } // User right-clicks on an item { combination: "right_click", has_focus: false, clicked_inside: true // Always true for right_click } ``` ## Python Integration ### Mouse Class The `Mouse` class provides a convenient way to add mouse support to elements. ```python from myfasthtml.controls.Mouse import Mouse from myfasthtml.core.commands import Command # Create mouse support for an element mouse = Mouse(parent_element) # Add combinations mouse.add("click", select_command) mouse.add("ctrl+click", toggle_command) mouse.add("right_click", context_menu_command) ``` ### Mouse.add() Method ```python def add(self, sequence: str, command: Command = None, *, hx_post: str = None, hx_get: str = None, hx_put: str = None, hx_delete: str = None, hx_patch: str = None, hx_target: str = None, hx_swap: str = None, hx_vals=None) ``` **Parameters**: - `sequence`: Mouse event sequence (e.g., "click", "ctrl+click", "click right_click") - `command`: Optional Command object for server-side action - `hx_post`, `hx_get`, etc.: HTMX URL parameters (override command) - `hx_target`: HTMX target selector (overrides command) - `hx_swap`: HTMX swap strategy (overrides command) - `hx_vals`: Additional HTMX values - dict or "js:functionName()" for dynamic values **Note**: - Named parameters (except `hx_vals`) override the command's parameters. - `hx_vals` is **merged** with command's values (stored in `hx-vals-extra`), preserving `c_id`. ### Usage Patterns **With Command only**: ```python mouse.add("click", my_command) ``` **With Command and overrides**: ```python # Command provides hx-post, but we override the target mouse.add("ctrl+click", my_command, hx_target="#other-result") ``` **Without Command (direct HTMX)**: ```python mouse.add("right_click", hx_post="/context-menu", hx_target="#menu", hx_swap="innerHTML") ``` **With dynamic values**: ```python mouse.add("shift+click", my_command, hx_vals="js:getClickPosition()") ``` ### Sequences ```python mouse = Mouse(element) mouse.add("click", single_click_command) mouse.add("click click", double_click_command) mouse.add("click right_click", special_action_command) ``` ### Multiple Elements ```python # Each element gets its own Mouse instance for item in items: mouse = Mouse(item) mouse.add("click", Command("select", "Select item", lambda i=item: select(i))) mouse.add("ctrl+click", Command("toggle", "Toggle item", lambda i=item: toggle(i))) ``` ### Dynamic hx-vals with JavaScript You can use `"js:functionName()"` to call a client-side JavaScript function that returns additional values to send with the request. The command's `c_id` is preserved. **Python**: ```python mouse.add("click", my_command, hx_vals="js:getClickContext()") ``` **Generated config** (internally): ```json { "hx-post": "/myfasthtml/commands", "hx-vals": {"c_id": "command_id"}, "hx-vals-extra": {"js": "getClickContext"} } ``` **JavaScript** (client-side): ```javascript // Function receives (event, element, combinationStr) function getClickContext(event, element, combination) { return { x: event.clientX, y: event.clientY, elementId: element.id, combo: combination }; } // Simple function - parameters are optional function getTimestamp() { return { ts: Date.now() }; } ``` **Values sent to server**: ```json { "c_id": "command_id", "x": 150, "y": 200, "elementId": "my-element", "combo": "click", "combination": "click", "has_focus": false, "is_inside": true } ``` You can also pass a static dict: ```python mouse.add("click", my_command, hx_vals={"extra_key": "extra_value"}) ``` ### Low-Level Usage (without Mouse class) For advanced use cases, you can generate the JavaScript directly: ```python import json combinations = { "click": { "hx-post": "/item/select" }, "ctrl+click": { "hx-post": "/item/select-multiple", "hx-vals": {"mode": "multi"} }, "right_click": { "hx-post": "/item/context-menu", "hx-target": "#context-menu", "hx-swap": "innerHTML" } } Script(f"add_mouse_support('{element_id}', '{json.dumps(combinations)}')") ``` ## Behavior Details ### Click vs Right-Click Behavior **IMPORTANT**: The library handles `click` and `right_click` differently: **`click` (global detection)**: - Triggers for ALL registered elements, regardless of where you click - Useful for "click outside to close" functionality (modals, dropdowns, popups) - Example: Modal registered with `click` → clicking anywhere on the page triggers the modal's click action **`right_click` (element-specific detection)**: - Triggers ONLY when you right-click on (or inside) the registered element - Right-clicking outside the element does nothing and shows browser's context menu - This preserves normal browser behavior while adding custom actions on your elements **Example use case**: ```javascript // Modal that closes when clicking anywhere add_mouse_support('modal', JSON.stringify({ "click": {"hx-post": "/close-modal"} // Triggers even if you click outside modal })); // Context menu that only appears on element add_mouse_support('item', JSON.stringify({ "right_click": {"hx-post": "/item-menu"} // Only triggers when right-clicking the item })); ``` ### Modifier Keys (Cross-Platform) - **Windows/Linux**: `ctrl+click` uses Ctrl key - **Mac**: `ctrl+click` uses Cmd (⌘) key OR Ctrl key - This follows standard web conventions for cross-platform compatibility ### Input Context Protection Mouse actions are **disabled** when clicking in input fields: - `` elements - `