diff --git a/src/myfasthtml/assets/README.md b/docs/Keyboard Support.md similarity index 85% rename from src/myfasthtml/assets/README.md rename to docs/Keyboard Support.md index 2bb7130..11a5802 100644 --- a/src/myfasthtml/assets/README.md +++ b/docs/Keyboard Support.md @@ -187,6 +187,7 @@ All other `hx-*` attributes are supported and will be converted to the appropria The library automatically adds these parameters to every request: - `combination` - The combination that triggered the action (e.g., "Ctrl+S") - `has_focus` - Boolean indicating if the element had focus +- `is_inside` - Boolean indicating if the focus is inside the element (element itself or any child) Example final request: ```javascript @@ -196,7 +197,8 @@ htmx.ajax('POST', '/save-url', { values: { extra: "data", // from hx-vals combination: "Ctrl+S", // automatic - has_focus: true // automatic + has_focus: true, // automatic + is_inside: true // automatic } }) ``` @@ -262,6 +264,55 @@ f"add_keyboard_support('modal', '{json.dumps(modal_combinations)}')" f"add_keyboard_support('editor', '{json.dumps(editor_combinations)}')" ``` +### Removing Keyboard Support + +When you no longer need keyboard support for an element: + +```python +# Remove keyboard support +f"remove_keyboard_support('{element_id}')" +``` + +**Behavior**: +- Removes the element from the keyboard registry +- If this was the last element, automatically detaches global event listeners +- Cleans up all associated state (timeouts, snapshots, etc.) +- Other elements continue to work normally + +**Example**: +```javascript +// Add support +add_keyboard_support('modal', '{"esc": {"hx-post": "/close"}}'); + +// Later, remove support +remove_keyboard_support('modal'); +// If no other elements remain, keyboard listeners are completely removed +``` + +## API Reference + +### add_keyboard_support(elementId, combinationsJson) + +Adds keyboard support to an element. + +**Parameters**: +- `elementId` (string): ID of the HTML element +- `combinationsJson` (string): JSON string of combinations with HTMX configs + +**Returns**: void + +### remove_keyboard_support(elementId) + +Removes keyboard 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 + ## Technical Details ### Trie-based Matching @@ -277,7 +328,7 @@ The library uses a prefix tree (trie) data structure: Configuration objects are mapped to htmx.ajax() calls: - `hx-*` attributes are converted to camelCase parameters - HTTP method is extracted from `hx-post`, `hx-get`, etc. -- `combination` and `has_focus` are automatically added to values +- `combination`, `has_focus`, and `is_inside` are automatically added to values - All standard HTMX options are supported ### Key Normalization diff --git a/docs/Mouse Support.md b/docs/Mouse Support.md new file mode 100644 index 0000000..d476659 --- /dev/null +++ b/docs/Mouse Support.md @@ -0,0 +1,439 @@ +# 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)); +``` + +## 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 + +### Basic Usage + +```python +combinations = { + "click": { + "hx-post": "/item/select" + }, + "ctrl+click": { + "hx-post": "/item/select-multiple", + "hx-vals": json.dumps({"mode": "multi"}) + }, + "right_click": { + "hx-post": "/item/context-menu", + "hx-target": "#context-menu", + "hx-swap": "innerHTML" + } +} + +f"add_mouse_support('{element_id}', '{json.dumps(combinations)}')" +``` + +### Sequences + +```python +combinations = { + "click": { + "hx-post": "/single-click" + }, + "click click": { + "hx-post": "/double-click-sequence" + }, + "click right_click": { + "hx-post": "/click-then-right-click" + } +} +``` + +### Multiple Elements + +```python +# Item 1 +item1_combinations = { + "click": {"hx-post": f"/item/1/select"}, + "ctrl+click": {"hx-post": f"/item/1/toggle"} +} +f"add_mouse_support('item-1', '{json.dumps(item1_combinations)}')" + +# Item 2 +item2_combinations = { + "click": {"hx-post": f"/item/2/select"}, + "ctrl+click": {"hx-post": f"/item/2/toggle"} +} +f"add_mouse_support('item-2', '{json.dumps(item2_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 +- `