Added first controls
This commit is contained in:
345
docs/Keyboard Support.md
Normal file
345
docs/Keyboard Support.md
Normal file
@@ -0,0 +1,345 @@
|
||||
# Keyboard Support - Test Instructions
|
||||
|
||||
## ⚠️ Breaking Change
|
||||
|
||||
**Version 2.0** uses HTMX configuration objects instead of simple URL strings. The old format is **not supported**.
|
||||
|
||||
**Old format (no longer supported)**:
|
||||
```javascript
|
||||
{"A": "/url"}
|
||||
```
|
||||
|
||||
**New format (required)**:
|
||||
```javascript
|
||||
{"A": {"hx-post": "/url"}}
|
||||
```
|
||||
|
||||
## Files
|
||||
|
||||
- `keyboard_support.js` - Main keyboard support library with smart timeout logic
|
||||
- `test_keyboard_support.html` - Test page to verify functionality
|
||||
|
||||
## Key Features
|
||||
|
||||
### Multiple Simultaneous Triggers
|
||||
|
||||
**IMPORTANT**: If multiple elements listen to the same combination, **ALL** of them will be triggered:
|
||||
|
||||
```javascript
|
||||
add_keyboard_support('modal', '{"esc": "/close-modal"}');
|
||||
add_keyboard_support('editor', '{"esc": "/cancel-edit"}');
|
||||
add_keyboard_support('sidebar', '{"esc": "/hide-sidebar"}');
|
||||
|
||||
// Pressing ESC will trigger all 3 URLs simultaneously
|
||||
```
|
||||
|
||||
This is crucial for use cases like the ESC key, which often needs to cancel multiple actions at once (close modal, cancel edit, hide panels, etc.).
|
||||
|
||||
### Smart Timeout Logic (Longest Match)
|
||||
|
||||
The library uses **a single global timeout** based on the sequence state, not on individual elements:
|
||||
|
||||
**Key principle**: If **any element** has a longer sequence possible, **all matching elements wait**.
|
||||
|
||||
Examples:
|
||||
|
||||
**Example 1**: Three elements, same combination
|
||||
```javascript
|
||||
add_keyboard_support('elem1', '{"esc": "/url1"}');
|
||||
add_keyboard_support('elem2', '{"esc": "/url2"}');
|
||||
add_keyboard_support('elem3', '{"esc": "/url3"}');
|
||||
// Press ESC → ALL 3 trigger immediately (no longer sequences exist)
|
||||
```
|
||||
|
||||
**Example 2**: Mixed - one has longer sequence
|
||||
```javascript
|
||||
add_keyboard_support('elem1', '{"A": "/url1"}');
|
||||
add_keyboard_support('elem2', '{"A": "/url2"}');
|
||||
add_keyboard_support('elem3', '{"A": "/url3", "A B": "/url3b"}');
|
||||
// Press A:
|
||||
// - elem3 has "A B" possible → EVERYONE WAITS 500ms
|
||||
// - If B arrives: only elem3 triggers with "A B"
|
||||
// - If timeout expires: elem1, elem2, elem3 ALL trigger with "A"
|
||||
```
|
||||
|
||||
**Example 3**: Different combinations
|
||||
```javascript
|
||||
add_keyboard_support('elem1', '{"A B": "/url1"}');
|
||||
add_keyboard_support('elem2', '{"C D": "/url2"}');
|
||||
// Press A: elem1 waits for B, elem2 not affected
|
||||
// Press C: elem2 waits for D, elem1 not affected
|
||||
```
|
||||
|
||||
The timeout is tied to the **sequence being typed**, not to individual elements.
|
||||
|
||||
### Smart Timeout Logic (Longest Match)
|
||||
|
||||
Keyboard shortcuts are **disabled** when typing in input fields:
|
||||
- `<input>` elements
|
||||
- `<textarea>` elements
|
||||
- Any `contenteditable` element
|
||||
|
||||
This ensures normal typing (Ctrl+C, Ctrl+A, etc.) works as expected in forms.
|
||||
|
||||
## How to Test
|
||||
|
||||
1. **Download both files** to the same directory
|
||||
2. **Open `test_keyboard_support.html`** in a web browser
|
||||
3. **Try the configured shortcuts**:
|
||||
- `a` - Simple key (waits if "A B" might follow)
|
||||
- `Ctrl+S` - Save combination (immediate)
|
||||
- `Ctrl+C` - Copy combination (waits because "Ctrl+C C" exists)
|
||||
- `A B` - Sequence (waits because "A B C" exists)
|
||||
- `A B C` - Triple sequence (triggers immediately)
|
||||
- `Ctrl+C C` - Press Ctrl+C together, release, then press C alone
|
||||
- `Ctrl+C Ctrl+C` - Press Ctrl+C, keep Ctrl, release C, press C again
|
||||
- `shift shift` - Press Shift twice in sequence
|
||||
- `esc` - Escape key (immediate)
|
||||
|
||||
4. **Test focus behavior**:
|
||||
- Click on the test element to focus it (turns blue)
|
||||
- Try shortcuts with focus
|
||||
- Click outside to remove focus
|
||||
- Try shortcuts without focus
|
||||
- The log shows whether the element had focus when triggered
|
||||
|
||||
5. **Test input protection**:
|
||||
- Try typing in the input field
|
||||
- Use Ctrl+C, Ctrl+A, etc. - should work normally
|
||||
- Shortcuts should NOT trigger while typing in input
|
||||
|
||||
## Expected Behavior
|
||||
|
||||
### Smart Timeout Examples
|
||||
|
||||
**Scenario 1**: Only "A" is configured (no element has "A B")
|
||||
- Press A → Triggers **immediately**
|
||||
|
||||
**Scenario 2**: At least one element has "A B"
|
||||
- Press A → **ALL elements with "A" wait 500ms**
|
||||
- If B pressed within 500ms → Only elements with "A B" trigger
|
||||
- If timeout expires → ALL elements with "A" trigger
|
||||
|
||||
**Scenario 3**: "A", "A B", and "A B C" all configured (same or different elements)
|
||||
- Press A → Waits (because "A B" exists)
|
||||
- Press B → Waits (because "A B C" exists)
|
||||
- Press C → Triggers "A B C" **immediately**
|
||||
|
||||
**Scenario 4**: Multiple elements, ESC on all
|
||||
```javascript
|
||||
add_keyboard_support('modal', '{"esc": "/close"}');
|
||||
add_keyboard_support('editor', '{"esc": "/cancel"}');
|
||||
```
|
||||
- Press ESC → **Both trigger simultaneously** (no longer sequences)
|
||||
|
||||
## Integration in Your Project
|
||||
|
||||
## Integration in Your Project
|
||||
|
||||
### Configuration Format
|
||||
|
||||
The library now uses **HTMX configuration objects** instead of simple URL strings:
|
||||
|
||||
```python
|
||||
# New format with HTMX configuration
|
||||
combinations = {
|
||||
"Ctrl+S": {
|
||||
"hx-post": "/save-url",
|
||||
"hx-target": "#result",
|
||||
"hx-swap": "innerHTML"
|
||||
},
|
||||
"A B": {
|
||||
"hx-post": "/sequence-url",
|
||||
"hx-vals": {"extra": "data"}
|
||||
},
|
||||
"esc": {
|
||||
"hx-get": "/cancel-url"
|
||||
}
|
||||
}
|
||||
|
||||
# This will generate the JavaScript call
|
||||
f"add_keyboard_support('{element_id}', '{json.dumps(combinations)}')"
|
||||
```
|
||||
|
||||
### Supported HTMX Attributes
|
||||
|
||||
You can use any HTMX attribute in the configuration object:
|
||||
|
||||
**HTTP Methods** (one required):
|
||||
- `hx-post` - POST request
|
||||
- `hx-get` - GET request
|
||||
- `hx-put` - PUT request
|
||||
- `hx-delete` - DELETE request
|
||||
- `hx-patch` - PATCH request
|
||||
|
||||
**Common Options**:
|
||||
- `hx-target` - Target element selector
|
||||
- `hx-swap` - Swap strategy (innerHTML, outerHTML, etc.)
|
||||
- `hx-vals` - Additional values to send (object)
|
||||
- `hx-headers` - Custom headers (object)
|
||||
- `hx-select` - Select specific content from response
|
||||
- `hx-confirm` - Confirmation message
|
||||
|
||||
All other `hx-*` attributes are supported and will be converted to the appropriate htmx.ajax() parameters.
|
||||
|
||||
### Automatic Parameters
|
||||
|
||||
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
|
||||
htmx.ajax('POST', '/save-url', {
|
||||
target: '#result',
|
||||
swap: 'innerHTML',
|
||||
values: {
|
||||
extra: "data", // from hx-vals
|
||||
combination: "Ctrl+S", // automatic
|
||||
has_focus: true, // automatic
|
||||
is_inside: true // automatic
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
## Integration Examples
|
||||
|
||||
### Basic Usage
|
||||
|
||||
```python
|
||||
combinations = {
|
||||
"Ctrl+S": {
|
||||
"hx-post": "/save"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### With Target and Swap
|
||||
|
||||
```python
|
||||
combinations = {
|
||||
"Ctrl+D": {
|
||||
"hx-delete": "/item",
|
||||
"hx-target": "#item-list",
|
||||
"hx-swap": "outerHTML"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### With Extra Values
|
||||
|
||||
```python
|
||||
combinations = {
|
||||
"Ctrl+N": {
|
||||
"hx-post": "/create",
|
||||
"hx-vals": json.dumps({"type": "quick", "mode": "keyboard"})
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Multiple Elements Example
|
||||
|
||||
```python
|
||||
# Modal close
|
||||
modal_combinations = {
|
||||
"esc": {
|
||||
"hx-post": "/modal/close",
|
||||
"hx-target": "#modal",
|
||||
"hx-swap": "outerHTML"
|
||||
}
|
||||
}
|
||||
|
||||
# Editor cancel
|
||||
editor_combinations = {
|
||||
"esc": {
|
||||
"hx-post": "/editor/cancel",
|
||||
"hx-target": "#editor",
|
||||
"hx-swap": "innerHTML"
|
||||
}
|
||||
}
|
||||
|
||||
# Both will trigger when ESC is pressed
|
||||
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
|
||||
|
||||
The library uses a prefix tree (trie) data structure:
|
||||
- Each node represents a keyboard snapshot (set of pressed keys)
|
||||
- Leaf nodes contain the HTMX configuration object
|
||||
- Intermediate nodes indicate longer sequences exist
|
||||
- Enables efficient O(n) matching where n is sequence length
|
||||
|
||||
### HTMX Integration
|
||||
|
||||
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`, `has_focus`, and `is_inside` are automatically added to values
|
||||
- All standard HTMX options are supported
|
||||
|
||||
### Key Normalization
|
||||
|
||||
- Case-insensitive: "ctrl" = "Ctrl" = "CTRL"
|
||||
- Mapped keys: "Control" → "ctrl", "Escape" → "esc", "Delete" → "del"
|
||||
- Simultaneous keys represented as sorted sets
|
||||
|
||||
## Notes
|
||||
|
||||
- The test page mocks `htmx.ajax` to display results in the console
|
||||
- In production, real AJAX calls will be made to your backend
|
||||
- Sequence timeout is 500ms between keys
|
||||
- Maximum 10 snapshots kept in history to prevent memory issues
|
||||
439
docs/Mouse Support.md
Normal file
439
docs/Mouse Support.md
Normal file
@@ -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:
|
||||
- `<input>` elements
|
||||
- `<textarea>` elements
|
||||
- Any `contenteditable` element
|
||||
|
||||
This ensures normal text selection and interaction works in forms.
|
||||
|
||||
### Right-Click Menu
|
||||
|
||||
The contextmenu (right-click menu) is prevented with `preventDefault()` when:
|
||||
- A `right_click` action is configured for the element
|
||||
- The element is NOT an input/textarea/contenteditable
|
||||
|
||||
### Event Bubbling
|
||||
|
||||
The library checks if the click target OR any parent element is registered:
|
||||
```html
|
||||
<div id="container">
|
||||
<button>Click me</button>
|
||||
</div>
|
||||
```
|
||||
If `container` is registered, clicking the button will trigger the container's actions.
|
||||
|
||||
## Examples
|
||||
|
||||
### Using Aliases
|
||||
|
||||
You can use `rclick` instead of `right_click` anywhere:
|
||||
|
||||
```javascript
|
||||
// These are equivalent
|
||||
const config1 = {
|
||||
"right_click": {"hx-post": "/menu"}
|
||||
};
|
||||
|
||||
const config2 = {
|
||||
"rclick": {"hx-post": "/menu"} // Shorter alias
|
||||
};
|
||||
|
||||
// Works in sequences too
|
||||
const config3 = {
|
||||
"click rclick": {"hx-post": "/sequence"}
|
||||
};
|
||||
|
||||
// Works with modifiers
|
||||
const config4 = {
|
||||
"ctrl+rclick": {"hx-post": "/ctrl-right-click"}
|
||||
};
|
||||
```
|
||||
|
||||
### Context Menu
|
||||
|
||||
```javascript
|
||||
const combinations = {
|
||||
"right_click": {
|
||||
"hx-post": "/show-context-menu",
|
||||
"hx-target": "#menu",
|
||||
"hx-swap": "innerHTML"
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
### Close Modal/Popup on Click Outside
|
||||
|
||||
Since `click` is detected globally (anywhere on the page), it's perfect for "click outside to close":
|
||||
|
||||
```javascript
|
||||
// Modal element
|
||||
const modalCombinations = {
|
||||
"click": {
|
||||
"hx-post": "/close-modal",
|
||||
"hx-target": "#modal",
|
||||
"hx-swap": "outerHTML"
|
||||
}
|
||||
};
|
||||
add_mouse_support('modal', JSON.stringify(modalCombinations));
|
||||
|
||||
// Now clicking ANYWHERE on the page will trigger the handler
|
||||
// Your backend receives:
|
||||
// - clicked_inside: true (if clicked on modal) → maybe keep it open
|
||||
// - clicked_inside: false (if clicked outside) → close it!
|
||||
```
|
||||
|
||||
**Backend example (Python/Flask)**:
|
||||
```python
|
||||
@app.route('/close-modal', methods=['POST'])
|
||||
def close_modal():
|
||||
clicked_inside = request.form.get('clicked_inside') == 'true'
|
||||
|
||||
if clicked_inside:
|
||||
# User clicked inside the modal - keep it open
|
||||
return render_template('modal_content.html')
|
||||
else:
|
||||
# User clicked outside - close the modal
|
||||
return '' # Empty response removes the modal
|
||||
```
|
||||
|
||||
**Note**: The click handler on the modal element will trigger for all clicks on the page, not just clicks on the modal itself. Use the `clicked_inside` parameter to determine the appropriate action.
|
||||
|
||||
### Multi-Select List
|
||||
|
||||
```javascript
|
||||
const combinations = {
|
||||
"click": {
|
||||
"hx-post": "/select-item",
|
||||
"hx-vals": {"mode": "single"}
|
||||
},
|
||||
"ctrl+click": {
|
||||
"hx-post": "/select-item",
|
||||
"hx-vals": {"mode": "toggle"}
|
||||
},
|
||||
"shift+click": {
|
||||
"hx-post": "/select-range",
|
||||
"hx-vals": {"mode": "range"}
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
### Interactive Canvas/Drawing
|
||||
|
||||
```javascript
|
||||
const combinations = {
|
||||
"click": {
|
||||
"hx-post": "/draw-point"
|
||||
},
|
||||
"ctrl+click": {
|
||||
"hx-post": "/draw-special"
|
||||
},
|
||||
"click click": {
|
||||
"hx-post": "/confirm-action"
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
### Drag-and-Drop Alternative
|
||||
|
||||
```javascript
|
||||
const combinations = {
|
||||
"click": {
|
||||
"hx-post": "/select-source"
|
||||
},
|
||||
"click click": {
|
||||
"hx-post": "/set-destination"
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Clicks not detected
|
||||
|
||||
- Verify the element exists and has the correct ID
|
||||
- Check browser console for errors
|
||||
- Ensure HTMX is loaded before mouse_support.js
|
||||
|
||||
### Right-click menu still appears
|
||||
|
||||
- Check if element is in input context (input/textarea)
|
||||
- Verify the combination is configured correctly
|
||||
- Check browser console for configuration errors
|
||||
|
||||
### Sequences not working
|
||||
|
||||
- Ensure clicks happen within 500ms timeout
|
||||
- Check if longer sequences exist (causes waiting)
|
||||
- Verify the combination string format (space-separated)
|
||||
|
||||
## Technical Details
|
||||
|
||||
### Architecture
|
||||
|
||||
- **Global listeners** on `document` for `click` and `contextmenu` events
|
||||
- **Tree-based matching** using prefix trees (same as keyboard support)
|
||||
- **Single timeout** for all elements (sequence-based, not element-based)
|
||||
- **Independent from keyboard support** (separate registry and timeouts)
|
||||
|
||||
### Performance
|
||||
|
||||
- Single event listener regardless of number of elements
|
||||
- O(n) matching where n is sequence length
|
||||
- Efficient memory usage with automatic cleanup
|
||||
|
||||
### Browser Compatibility
|
||||
|
||||
- Modern browsers (ES6+ required)
|
||||
- Chrome, Firefox, Safari, Edge
|
||||
- Requires HTMX library
|
||||
|
||||
## Notes
|
||||
|
||||
- Timeout value is the same as keyboard support (500ms) but in separate variable
|
||||
- Can be used independently or alongside keyboard support
|
||||
- Does not interfere with normal mouse behavior in inputs
|
||||
- Element must exist in DOM when `add_mouse_support()` is called
|
||||
- **Alias**: `rclick` can be used interchangeably with `right_click` for shorter syntax
|
||||
Reference in New Issue
Block a user