Skip to content

Commit e84e376

Browse files
Allow preventing default handlers for key events (zauberzeug#5222)
### Motivation In zauberzeug#4924 @samuller mentioned that the "/" key on the NiceGUI website doesn't work flawlessly to open the search dialog on Firefox because the default handler opens Firefox' built-in search field. We also discussed how to bring the `preventDefault()` and `stopPropagation()` functions to `ui.keyboard`. ### Implementation - This PR adds the original JavaScript event object to the argument dictionary which is passed to `js_handler`. - A new demo shows how to use `.on('key', ..., js_handler=...)` to handle key events on the client and conditionally call `preventDefault()` or `stopPropagation()`. - A similar `js_handler` function is used to improve the "/" key behavior on the NiceGUI website. - The formatting of some version specifiers is fixed. ### Progress - [x] I chose a meaningful title that completes the sentence: "If applied, this PR will..." - [x] The implementation is complete. - [x] Pytests are not necessary. - [x] Documentation has been added.
1 parent f5159b8 commit e84e376

File tree

5 files changed

+29
-11
lines changed

5 files changed

+29
-11
lines changed

nicegui/elements/button_dropdown.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ def __init__(self,
5555
def on_click(self, callback: Handler[ClickEventArguments]) -> Self:
5656
"""Add a callback to be invoked when the dropdown button is clicked.
5757
58-
**Added in version 2.22.0**
58+
*Added in version 2.22.0*
5959
"""
6060
self.on('click', lambda _: handle_event(callback, ClickEventArguments(sender=self, client=self.client)), [])
6161
return self

nicegui/elements/fab.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ def __init__(self, icon: str, *,
5959
An action that can be added to a floating action button (FAB).
6060
This element is based on Quasar's `QFabAction <https://quasar.dev/vue-components/floating-action-button#qfabaction-api>`_ component.
6161
62-
**Added in version 2.22.0**
62+
*Added in version 2.22.0*
6363
6464
:param icon: icon to be displayed on the action button
6565
:param label: optional label for the action button

nicegui/elements/keyboard.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ export default {
1212
if (evt.repeat && !this.repeating) return;
1313

1414
this.$emit("key", {
15+
event: evt,
1516
action: event,
1617
altKey: evt.altKey,
1718
ctrlKey: evt.ctrlKey,

website/documentation/content/keyboard_documentation.py

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,4 +28,23 @@ def handle_key(e: KeyEventArguments):
2828
ui.checkbox('Track key events').bind_value_to(keyboard, 'active')
2929

3030

31+
@doc.demo('Prevent default and stop propagation', '''
32+
You can use the `js_handler` parameter of the `on` method to control how an event is handled on the client side.
33+
To prevent the default behavior, you can call the `preventDefault` method of the `event` object.
34+
To stop the propagation of the event, you could also call the `stopPropagation` method of the `event` object.
35+
36+
*Added in version 3.1.0*
37+
''')
38+
def prevent_default() -> None:
39+
ui.label('Select via Ctrl-A or Cmd-A is disabled')
40+
41+
ui.keyboard() \
42+
.on('key', lambda: ui.notify('Select all prevented.'), js_handler='''(e) => {
43+
if (e.key === 'a' && (e.ctrlKey || e.metaKey) && e.action === 'keydown') {
44+
emit(e);
45+
e.event.preventDefault();
46+
}
47+
}''')
48+
49+
3150
doc.reference(ui.keyboard)

website/search.py

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -39,20 +39,18 @@ def __init__(self) -> None:
3939
.props('padding="2px 8px" outline size=sm color=grey-5').classes('shadow')
4040
ui.separator()
4141
self.results = ui.element('q-list').classes('w-full').props('separator')
42-
ui.keyboard(self.handle_keypress)
42+
ui.keyboard().on('key', self.dialog.open, js_handler='''(e) => {
43+
if (e.action !== 'keydown') return;
44+
if (e.key === '/' || (e.key === 'k' && (e.ctrlKey || e.metaKey))) {
45+
emit(e);
46+
e.event.preventDefault();
47+
}
48+
}''')
4349

4450
def create_button(self) -> ui.button:
4551
return ui.button(on_click=self.dialog.open, icon='search').props('flat color=white') \
4652
.tooltip('Press Ctrl+K or / to search the documentation')
4753

48-
def handle_keypress(self, e: events.KeyEventArguments) -> None:
49-
if not e.action.keydown:
50-
return
51-
if e.key == '/':
52-
self.dialog.open()
53-
if e.key == 'k' and (e.modifiers.ctrl or e.modifiers.meta):
54-
self.dialog.open()
55-
5654
def handle_input(self, e: events.ValueChangeEventArguments) -> None:
5755
async def handle_input() -> None:
5856
with self.results:

0 commit comments

Comments
 (0)