mirror of
https://github.com/rio-labs/rio.git
synced 2025-12-20 04:09:44 -06:00
add event order control to KeyEventListener
This commit is contained in:
@@ -10,9 +10,7 @@ Changes:
|
|||||||
event handlers
|
event handlers
|
||||||
- `TextStyle` no longer has default values, omitted values are left unchanged
|
- `TextStyle` no longer has default values, omitted values are left unchanged
|
||||||
instead
|
instead
|
||||||
|
|
||||||
Deprecations:
|
Deprecations:
|
||||||
|
|
||||||
- The `CursorStyle` enum is deprecated in favor of string literals
|
- The `CursorStyle` enum is deprecated in favor of string literals
|
||||||
|
|
||||||
Bugfixes:
|
Bugfixes:
|
||||||
@@ -24,6 +22,9 @@ Additions:
|
|||||||
|
|
||||||
- `NumberInput` can now evaluate math expressions
|
- `NumberInput` can now evaluate math expressions
|
||||||
- `PointerEventListener` can now listen to only specific button events
|
- `PointerEventListener` can now listen to only specific button events
|
||||||
|
- `PointerEventListener` and `KeyEventListener` now support the `event_order`
|
||||||
|
parameter to control whether events are handled before or after child
|
||||||
|
components
|
||||||
- `KeyEventListener` can now listen to only specifc hotkeys
|
- `KeyEventListener` can now listen to only specifc hotkeys
|
||||||
- `Calendar` now has a `is_sensitive` parameter
|
- `Calendar` now has a `is_sensitive` parameter
|
||||||
- New component: `rio.PdfViewer`
|
- New component: `rio.PdfViewer`
|
||||||
|
|||||||
@@ -697,12 +697,15 @@ export type KeyEventListenerState = KeyboardFocusableComponentState & {
|
|||||||
reportKeyDown: KeyCombination[] | true;
|
reportKeyDown: KeyCombination[] | true;
|
||||||
reportKeyUp: KeyCombination[] | true;
|
reportKeyUp: KeyCombination[] | true;
|
||||||
reportKeyPress: KeyCombination[] | true;
|
reportKeyPress: KeyCombination[] | true;
|
||||||
|
event_order: "before-child" | "after-child";
|
||||||
};
|
};
|
||||||
|
|
||||||
export class KeyEventListenerComponent extends KeyboardFocusableComponent<KeyEventListenerState> {
|
export class KeyEventListenerComponent extends KeyboardFocusableComponent<KeyEventListenerState> {
|
||||||
private keyDownCombinations: Set<string> | true;
|
private keyDownCombinations: Set<string> | true;
|
||||||
private keyUpCombinations: Set<string> | true;
|
private keyUpCombinations: Set<string> | true;
|
||||||
private keyPressCombinations: Set<string> | true;
|
private keyPressCombinations: Set<string> | true;
|
||||||
|
private onKeyDownBound: ((e: KeyboardEvent) => void) | null = null;
|
||||||
|
private onKeyUpBound: ((e: KeyboardEvent) => void) | null = null;
|
||||||
|
|
||||||
createElement(context: ComponentStatesUpdateContext): HTMLElement {
|
createElement(context: ComponentStatesUpdateContext): HTMLElement {
|
||||||
let element = document.createElement("div");
|
let element = document.createElement("div");
|
||||||
@@ -738,30 +741,54 @@ export class KeyEventListenerComponent extends KeyboardFocusableComponent<KeyEve
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check if we need to update event listeners
|
||||||
|
if (
|
||||||
|
deltaState.reportKeyDown !== undefined ||
|
||||||
|
deltaState.reportKeyPress !== undefined ||
|
||||||
|
deltaState.event_order !== undefined
|
||||||
|
) {
|
||||||
let reportKeyDown =
|
let reportKeyDown =
|
||||||
this.keyDownCombinations === true ||
|
this.keyDownCombinations === true ||
|
||||||
this.keyDownCombinations.size > 0;
|
this.keyDownCombinations.size > 0;
|
||||||
let reportKeyUp =
|
|
||||||
this.keyUpCombinations === true || this.keyUpCombinations.size > 0;
|
|
||||||
let reportKeyPress =
|
let reportKeyPress =
|
||||||
this.keyPressCombinations === true ||
|
this.keyPressCombinations === true ||
|
||||||
this.keyPressCombinations.size > 0;
|
this.keyPressCombinations.size > 0;
|
||||||
|
let eventOrder = deltaState.event_order ?? this.state.event_order;
|
||||||
|
|
||||||
if (reportKeyDown || reportKeyPress) {
|
this.onKeyDownBound = this._updateEventListener(
|
||||||
this.element.onkeydown = (e: KeyboardEvent) => {
|
"keydown",
|
||||||
this.handleKeyEvent(e, "KeyPress", this.keyPressCombinations);
|
reportKeyDown || reportKeyPress,
|
||||||
|
eventOrder,
|
||||||
|
this.onKeyDownBound,
|
||||||
|
(e: KeyboardEvent) => {
|
||||||
|
this.handleKeyEvent(
|
||||||
|
e,
|
||||||
|
"KeyPress",
|
||||||
|
this.keyPressCombinations
|
||||||
|
);
|
||||||
this.handleKeyEvent(e, "KeyDown", this.keyDownCombinations);
|
this.handleKeyEvent(e, "KeyDown", this.keyDownCombinations);
|
||||||
};
|
}
|
||||||
} else {
|
);
|
||||||
this.element.onkeydown = null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (reportKeyUp) {
|
if (
|
||||||
this.element.onkeyup = (e: KeyboardEvent) => {
|
deltaState.reportKeyUp !== undefined ||
|
||||||
|
deltaState.event_order !== undefined
|
||||||
|
) {
|
||||||
|
let reportKeyUp =
|
||||||
|
this.keyUpCombinations === true ||
|
||||||
|
this.keyUpCombinations.size > 0;
|
||||||
|
let eventOrder = deltaState.event_order ?? this.state.event_order;
|
||||||
|
|
||||||
|
this.onKeyUpBound = this._updateEventListener(
|
||||||
|
"keyup",
|
||||||
|
reportKeyUp,
|
||||||
|
eventOrder,
|
||||||
|
this.onKeyUpBound,
|
||||||
|
(e: KeyboardEvent) => {
|
||||||
this.handleKeyEvent(e, "KeyUp", this.keyUpCombinations);
|
this.handleKeyEvent(e, "KeyUp", this.keyUpCombinations);
|
||||||
};
|
}
|
||||||
} else {
|
);
|
||||||
this.element.onkeyup = null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
this.replaceOnlyChild(context, deltaState.content);
|
this.replaceOnlyChild(context, deltaState.content);
|
||||||
@@ -804,6 +831,32 @@ export class KeyEventListenerComponent extends KeyboardFocusableComponent<KeyEve
|
|||||||
...encodedEvent,
|
...encodedEvent,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Helper method to manage event listeners with capture phase support
|
||||||
|
private _updateEventListener(
|
||||||
|
eventName: string,
|
||||||
|
shouldInstall: boolean,
|
||||||
|
eventOrder: "before-child" | "after-child",
|
||||||
|
currentHandler: ((e: KeyboardEvent) => void) | null,
|
||||||
|
callbackMethod: (e: KeyboardEvent) => void
|
||||||
|
): ((e: KeyboardEvent) => void) | null {
|
||||||
|
// Remove existing listener if it exists
|
||||||
|
if (currentHandler !== null) {
|
||||||
|
this.element.removeEventListener(eventName, currentHandler, {
|
||||||
|
capture: this.state.event_order === "before-child",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!shouldInstall) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Install new listener with current capture setting
|
||||||
|
this.element.addEventListener(eventName, callbackMethod, {
|
||||||
|
capture: eventOrder === "before-child",
|
||||||
|
});
|
||||||
|
return callbackMethod;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function keyCombinationsSetFromDeltaState(
|
function keyCombinationsSetFromDeltaState(
|
||||||
|
|||||||
@@ -679,6 +679,11 @@ class KeyEventListener(KeyboardFocusableFundamentalComponent):
|
|||||||
`on_key_up`: A function to call when a key is released.
|
`on_key_up`: A function to call when a key is released.
|
||||||
|
|
||||||
`on_key_press`: A function to call repeatedly while a key is held down.
|
`on_key_press`: A function to call repeatedly while a key is held down.
|
||||||
|
|
||||||
|
`event_order`: Controls when this listener receives events relative to
|
||||||
|
its child components. When `"before-child"`, this listener's handlers
|
||||||
|
are called before any child component handlers. When `"after-child"`
|
||||||
|
(default), child components receive events first, then this listener.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
content: rio.Component
|
content: rio.Component
|
||||||
@@ -695,6 +700,7 @@ class KeyEventListener(KeyboardFocusableFundamentalComponent):
|
|||||||
rio.EventHandler[KeyPressEvent]
|
rio.EventHandler[KeyPressEvent]
|
||||||
| t.Mapping[KeyCombination, rio.EventHandler[KeyPressEvent]]
|
| t.Mapping[KeyCombination, rio.EventHandler[KeyPressEvent]]
|
||||||
) = None
|
) = None
|
||||||
|
event_order: t.Literal["before-child", "after-child"] = "after-child"
|
||||||
|
|
||||||
def __post_init__(self):
|
def __post_init__(self):
|
||||||
# TODO: These values are never updated, which is a problem if someone
|
# TODO: These values are never updated, which is a problem if someone
|
||||||
|
|||||||
Reference in New Issue
Block a user