Implemented enable/disable for keyboard support
This commit is contained in:
@@ -100,6 +100,57 @@ add_keyboard_support('elem2', '{"C D": "/url2"}');
|
||||
|
||||
The timeout is tied to the **sequence being typed**, not to individual elements.
|
||||
|
||||
### Enabling and Disabling Combinations
|
||||
|
||||
Each combination can be enabled or disabled independently. A disabled combination is registered and tracked, but its action is never triggered.
|
||||
|
||||
| State | Behavior |
|
||||
|-------|----------|
|
||||
| `enabled=True` (default) | Combination triggers normally |
|
||||
| `enabled=False` | Combination is silently ignored when pressed |
|
||||
|
||||
**Setting the initial state at registration:**
|
||||
|
||||
```python
|
||||
# Enabled by default
|
||||
keyboard.add("ctrl+s", self.commands.save())
|
||||
|
||||
# Disabled at startup
|
||||
keyboard.add("ctrl+d", self.commands.delete(), enabled=False)
|
||||
```
|
||||
|
||||
**Toggling dynamically at runtime:**
|
||||
|
||||
Use `mk_enable()` and `mk_disable()` to change the state from a server response. Both methods return an out-of-band HTMX element (`hx-swap-oob`) that updates the DOM without a full page reload.
|
||||
|
||||
```python
|
||||
# Enable a combination (e.g., once an item is selected)
|
||||
def handle_select(self):
|
||||
item = ...
|
||||
return item.render(), self.keyboard.mk_enable("ctrl+d")
|
||||
|
||||
# Disable a combination (e.g., when nothing is selected)
|
||||
def handle_deselect(self):
|
||||
return self.keyboard.mk_disable("ctrl+d")
|
||||
```
|
||||
|
||||
The enabled state is stored in a hidden control `<div>` rendered alongside the keyboard script. The JavaScript reads this state before triggering any action.
|
||||
|
||||
**State at a glance:**
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────┐
|
||||
│ Keyboard control div (hidden) │
|
||||
│ ┌──────────────────────────────┐ │
|
||||
│ │ div [data-combination="esc"] │ │
|
||||
│ │ [data-enabled="true"] │ │
|
||||
│ ├──────────────────────────────┤ │
|
||||
│ │ div [data-combination="ctrl+d"] │ │
|
||||
│ │ [data-enabled="false"] │ │
|
||||
│ └──────────────────────────────┘ │
|
||||
└─────────────────────────────────────┘
|
||||
```
|
||||
|
||||
### Smart Timeout Logic (Longest Match)
|
||||
|
||||
Keyboard shortcuts are **disabled** when typing in input fields:
|
||||
@@ -364,16 +415,56 @@ remove_keyboard_support('modal');
|
||||
|
||||
## API Reference
|
||||
|
||||
### add_keyboard_support(elementId, combinationsJson)
|
||||
### add_keyboard_support(elementId, controlDivId, combinationsJson)
|
||||
|
||||
Adds keyboard support to an element.
|
||||
|
||||
**Parameters**:
|
||||
- `elementId` (string): ID of the HTML element
|
||||
- `elementId` (string): ID of the HTML element to watch for key events
|
||||
- `controlDivId` (string): ID of the keyboard control div rendered by `Keyboard.render()`, used to look up enabled/disabled state at runtime
|
||||
- `combinationsJson` (string): JSON string of combinations with HTMX configs
|
||||
|
||||
**Returns**: void
|
||||
|
||||
### Python Component Methods
|
||||
|
||||
These methods are available on the `Keyboard` instance.
|
||||
|
||||
#### add(sequence, command, require_inside=True, enabled=True)
|
||||
|
||||
Registers a key combination.
|
||||
|
||||
| Parameter | Type | Description | Default |
|
||||
|-----------|------|-------------|---------|
|
||||
| `sequence` | `str` | Key combination string, e.g. `"ctrl+s"`, `"esc"`, `"a b"` | — |
|
||||
| `command` | `Command` | Command to execute when the combination is triggered | — |
|
||||
| `require_inside` | `bool` | If `True`, only triggers when focus is inside the element | `True` |
|
||||
| `enabled` | `bool` | Whether the combination is active at render time | `True` |
|
||||
|
||||
**Returns**: `self` (chainable)
|
||||
|
||||
#### mk_enable(sequence)
|
||||
|
||||
Returns an out-of-band HTMX element that enables a combination at runtime.
|
||||
|
||||
| Parameter | Type | Description |
|
||||
|-----------|------|-------------|
|
||||
| `sequence` | `str` | Key combination to enable, must match exactly what was passed to `add()` |
|
||||
|
||||
**Returns**: `Div` with `hx-swap-oob="true"`
|
||||
|
||||
#### mk_disable(sequence)
|
||||
|
||||
Returns an out-of-band HTMX element that disables a combination at runtime.
|
||||
|
||||
| Parameter | Type | Description |
|
||||
|-----------|------|-------------|
|
||||
| `sequence` | `str` | Key combination to disable, must match exactly what was passed to `add()` |
|
||||
|
||||
**Returns**: `Div` with `hx-swap-oob="true"`
|
||||
|
||||
---
|
||||
|
||||
### remove_keyboard_support(elementId)
|
||||
|
||||
Removes keyboard support from an element.
|
||||
|
||||
@@ -181,13 +181,13 @@ Users can rename nodes via the edit button:
|
||||
|
||||
```python
|
||||
# Programmatically start rename
|
||||
tree._start_rename("node-id")
|
||||
tree.handle_start_rename("node-id")
|
||||
|
||||
# Save rename
|
||||
tree._save_rename("node-id", "New Label")
|
||||
tree.handle_save_rename("node-id", "New Label")
|
||||
|
||||
# Cancel rename
|
||||
tree._cancel_rename()
|
||||
tree.handle_cancel_rename()
|
||||
```
|
||||
|
||||
### Deleting Nodes
|
||||
@@ -201,7 +201,7 @@ Users can delete nodes via the delete button:
|
||||
|
||||
```python
|
||||
# Programmatically delete node
|
||||
tree._delete_node("node-id") # Raises ValueError if node has children
|
||||
tree.handle_delete_node("node-id") # Raises ValueError if node has children
|
||||
```
|
||||
|
||||
## Content System
|
||||
@@ -449,18 +449,20 @@ tree = TreeView(parent=root_instance, _id="dynamic-tree")
|
||||
root = TreeNode(id="root", label="Tasks", type="folder")
|
||||
tree.add_node(root)
|
||||
|
||||
|
||||
# Function to handle selection
|
||||
def on_node_selected(node_id):
|
||||
# Custom logic when node is selected
|
||||
node = tree._state.items[node_id]
|
||||
tree._select_node(node_id)
|
||||
# Custom logic when node is selected
|
||||
node = tree._state.items[node_id]
|
||||
tree.handle_select_node(node_id)
|
||||
|
||||
# Update a detail panel elsewhere in the UI
|
||||
return Div(
|
||||
H3(f"Selected: {node.label}"),
|
||||
P(f"Type: {node.type}"),
|
||||
P(f"Children: {len(node.children)}")
|
||||
)
|
||||
|
||||
# Update a detail panel elsewhere in the UI
|
||||
return Div(
|
||||
H3(f"Selected: {node.label}"),
|
||||
P(f"Type: {node.type}"),
|
||||
P(f"Children: {len(node.children)}")
|
||||
)
|
||||
|
||||
# Override select command with custom handler
|
||||
# (In practice, you'd extend the Commands class or use event callbacks)
|
||||
|
||||
Reference in New Issue
Block a user