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:
211
changelog.md
211
changelog.md
@@ -4,171 +4,172 @@
|
|||||||
|
|
||||||
Changes:
|
Changes:
|
||||||
|
|
||||||
- Components that depend on session attributes (like `active_page_url`) or
|
- Components that depend on session attributes (like `active_page_url`) or
|
||||||
session attachments are now automatically rebuilt when those values change
|
session attachments are now automatically rebuilt when those values change
|
||||||
- Components are now rebuilt immediately after a state change, not just after
|
- Components are now rebuilt immediately after a state change, not just after
|
||||||
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:
|
||||||
|
|
||||||
- Fix `rio.Revealer` not stretching its child
|
- Fix `rio.Revealer` not stretching its child
|
||||||
- Fix various components propagating click events
|
- Fix various components propagating click events
|
||||||
|
|
||||||
Additions:
|
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
|
||||||
- `KeyEventListener` can now listen to only specifc hotkeys
|
- `PointerEventListener` and `KeyEventListener` now support the `event_order`
|
||||||
- `Calendar` now has a `is_sensitive` parameter
|
parameter to control whether events are handled before or after child
|
||||||
- New component: `rio.PdfViewer`
|
components
|
||||||
- `rio.Image` now has a loading animation
|
- `KeyEventListener` can now listen to only specifc hotkeys
|
||||||
- Added `accessibility_role` parameter to all components
|
- `Calendar` now has a `is_sensitive` parameter
|
||||||
- Added 'accessibility_relationship' parameter to `Link` component
|
- New component: `rio.PdfViewer`
|
||||||
- Added `auto_focus` parameter to input components
|
- `rio.Image` now has a loading animation
|
||||||
- Added `Session.pick_folder`
|
- Added `accessibility_role` parameter to all components
|
||||||
- Added `Font.from_google_fonts` and `Font.from_css_file`
|
- Added 'accessibility_relationship' parameter to `Link` component
|
||||||
- Added `rio.HttpOnly`
|
- Added `auto_focus` parameter to input components
|
||||||
- Added `text_style` parameter to `rio.TextInput`
|
- Added `Session.pick_folder`
|
||||||
- Experimental additions:
|
- Added `Font.from_google_fonts` and `Font.from_css_file`
|
||||||
- `rio.List`
|
- Added `rio.HttpOnly`
|
||||||
- `rio.Dict`
|
- Added `text_style` parameter to `rio.TextInput`
|
||||||
- `rio.Set`
|
- Experimental additions:
|
||||||
- `rio.Dataclass`
|
- `rio.List`
|
||||||
|
- `rio.Dict`
|
||||||
|
- `rio.Set`
|
||||||
|
- `rio.Dataclass`
|
||||||
|
|
||||||
## 0.11
|
## 0.11
|
||||||
|
|
||||||
- Added `tile` fill mode to `rio.ImageFill`
|
- Added `tile` fill mode to `rio.ImageFill`
|
||||||
- `Component.force_refresh` is now synchronous
|
- `Component.force_refresh` is now synchronous
|
||||||
- Added `tile` fill mode to `rio.ImageFill`
|
- Added `tile` fill mode to `rio.ImageFill`
|
||||||
- Colors now use Oklab instead of RGB
|
- Colors now use Oklab instead of RGB
|
||||||
- Breaking: `rio.Color.hex` now returns a 6-digit hex code instead of an
|
- Breaking: `rio.Color.hex` now returns a 6-digit hex code instead of an
|
||||||
8-digit one. Use `rio.Color.hexa` to get the old behavior.
|
8-digit one. Use `rio.Color.hexa` to get the old behavior.
|
||||||
- Dialogs now apply a style by default
|
- Dialogs now apply a style by default
|
||||||
- `rio.Drawer` now sizes itself to not only fit the anchor, but also the
|
- `rio.Drawer` now sizes itself to not only fit the anchor, but also the
|
||||||
drawer content
|
drawer content
|
||||||
- `rio.Popup` now accepts `user_closable` and `modal`, just like dialogs
|
- `rio.Popup` now accepts `user_closable` and `modal`, just like dialogs
|
||||||
- more styling options for cells in `rio.Table`
|
- more styling options for cells in `rio.Table`
|
||||||
|
|
||||||
- Expose additional platform information:
|
- Expose additional platform information:
|
||||||
|
|
||||||
- `rio.Session.screen_width`
|
- `rio.Session.screen_width`
|
||||||
- `rio.Session.screen_height`
|
- `rio.Session.screen_height`
|
||||||
- `rio.Session.pixels_per_font_height`
|
- `rio.Session.pixels_per_font_height`
|
||||||
- `rio.Session.scroll_bar_size`
|
- `rio.Session.scroll_bar_size`
|
||||||
- `rio.Session.primary_pointer_type`
|
- `rio.Session.primary_pointer_type`
|
||||||
|
|
||||||
- Themes now take an additional `header_font` parameter
|
- Themes now take an additional `header_font` parameter
|
||||||
|
|
||||||
- Breaking: Gradient stops can now be specified just as colors and Rio will
|
- Breaking: Gradient stops can now be specified just as colors and Rio will
|
||||||
infer their position (breaking, because the stops must be ordered now)
|
infer their position (breaking, because the stops must be ordered now)
|
||||||
|
|
||||||
- add icons for common brands
|
- add icons for common brands
|
||||||
|
|
||||||
## ???
|
## ???
|
||||||
|
|
||||||
- New styles for input boxes: "rounded" and "pill"
|
- New styles for input boxes: "rounded" and "pill"
|
||||||
- Improved mobile support: Dragging is now much smoother
|
- Improved mobile support: Dragging is now much smoother
|
||||||
- Improved tables
|
- Improved tables
|
||||||
- `rio run` now also works when using `as_fastapi`
|
- `rio run` now also works when using `as_fastapi`
|
||||||
|
|
||||||
## 0.10
|
## 0.10
|
||||||
|
|
||||||
- `rio.Dropdown` will now open a fullscreen popup on mobile devices
|
- `rio.Dropdown` will now open a fullscreen popup on mobile devices
|
||||||
- `rio.MediaPlayer` now also triggers the `on_playback_end` event when the
|
- `rio.MediaPlayer` now also triggers the `on_playback_end` event when the
|
||||||
video loops
|
video loops
|
||||||
- experimental support for base-URL
|
- experimental support for base-URL
|
||||||
- dialogs!
|
- dialogs!
|
||||||
- dialogs can now store a result value similar to futures
|
- dialogs can now store a result value similar to futures
|
||||||
- `rio.Text.wrap` is now `rio.Text.overflow`. Same for markdown.
|
- `rio.Text.wrap` is now `rio.Text.overflow`. Same for markdown.
|
||||||
- removed `rio.Popup.on_open_or_close`. This event never actually fired.
|
- removed `rio.Popup.on_open_or_close`. This event never actually fired.
|
||||||
- `rio.Link` can now optionally display an icon
|
- `rio.Link` can now optionally display an icon
|
||||||
- Rio will automatically create basic navigation for you, if your app has more
|
- Rio will automatically create basic navigation for you, if your app has more
|
||||||
than one page
|
than one page
|
||||||
- Updated button styles: Added `colored-text` and renamed `plain` ->
|
- Updated button styles: Added `colored-text` and renamed `plain` ->
|
||||||
`plain-text`
|
`plain-text`
|
||||||
- Methods for creating dialogs are now in `rio.Session` rather than
|
- Methods for creating dialogs are now in `rio.Session` rather than
|
||||||
`rio.Component`.
|
`rio.Component`.
|
||||||
- Page rework
|
- Page rework
|
||||||
- Add `rio.Redirect`
|
- Add `rio.Redirect`
|
||||||
- Still missing automatic page scan
|
- Still missing automatic page scan
|
||||||
- New experimental `rio.FilePickerArea` component
|
- New experimental `rio.FilePickerArea` component
|
||||||
|
|
||||||
## 0.9.2
|
## 0.9.2
|
||||||
|
|
||||||
- restyled `rio.Switch`
|
- restyled `rio.Switch`
|
||||||
- New ~~experimental~~ broken component `AspectRatioContainer`
|
- New ~~experimental~~ broken component `AspectRatioContainer`
|
||||||
|
|
||||||
## 0.9.1
|
## 0.9.1
|
||||||
|
|
||||||
- added gain_focus / lose_focus events to TextInput and NumberInput
|
- added gain_focus / lose_focus events to TextInput and NumberInput
|
||||||
- `.rioignore` has been superseeded by the new `project-files` setting in
|
- `.rioignore` has been superseeded by the new `project-files` setting in
|
||||||
`rio.toml`
|
`rio.toml`
|
||||||
- values in `rio.toml` are now written in kebab-case instead of
|
- values in `rio.toml` are now written in kebab-case instead of
|
||||||
all_lower_case. Rio will still recognize the old names and automatically fix
|
all_lower_case. Rio will still recognize the old names and automatically fix
|
||||||
them for you.
|
them for you.
|
||||||
- deprecated `light` parameter of `Theme.from_color`, has been superseded by
|
- deprecated `light` parameter of `Theme.from_color`, has been superseded by
|
||||||
`mode`
|
`mode`
|
||||||
- Tooltips now default to `position="auto"`
|
- Tooltips now default to `position="auto"`
|
||||||
- Icons now use `_` instead of `-` in their names. This brings them more in line
|
- Icons now use `_` instead of `-` in their names. This brings them more in line
|
||||||
with Python naming conventions
|
with Python naming conventions
|
||||||
- Checkbox restyling
|
- Checkbox restyling
|
||||||
|
|
||||||
## 0.9
|
## 0.9
|
||||||
|
|
||||||
- Buttons now have a smaller minimum size when using a `rio.Component` as
|
- Buttons now have a smaller minimum size when using a `rio.Component` as
|
||||||
content
|
content
|
||||||
- `FrostedGlassFill` added (Contributed by MiniTT)
|
- `FrostedGlassFill` added (Contributed by MiniTT)
|
||||||
- added `@rio.event.on_window_size_change`
|
- added `@rio.event.on_window_size_change`
|
||||||
- popups now default to the "hud" color
|
- popups now default to the "hud" color
|
||||||
- popups and tooltips are no longer cut off by other components
|
- popups and tooltips are no longer cut off by other components
|
||||||
- Add HTML meta tags
|
- Add HTML meta tags
|
||||||
- Add functions for reading and writing clipboard contents to the `Session`
|
- Add functions for reading and writing clipboard contents to the `Session`
|
||||||
(Contributed by MiniTT)
|
(Contributed by MiniTT)
|
||||||
- The color of drawers is now configurable, and also sets the theme context
|
- The color of drawers is now configurable, and also sets the theme context
|
||||||
- added `Calendar` component
|
- added `Calendar` component
|
||||||
- added `DateInput` component
|
- added `DateInput` component
|
||||||
- massive dev-tools overhaul
|
- massive dev-tools overhaul
|
||||||
- new (but experimental) `Switcher` component
|
- new (but experimental) `Switcher` component
|
||||||
- TextInputs now update their text in real-time
|
- TextInputs now update their text in real-time
|
||||||
- `rio run` no longer opens a browser
|
- `rio run` no longer opens a browser
|
||||||
- `rio.HTML` components now execute embedded `<script>` nodes
|
- `rio.HTML` components now execute embedded `<script>` nodes
|
||||||
- added `Checkbox` Component
|
- added `Checkbox` Component
|
||||||
- `FlowContainer` now has a convenience `spacing` parameter which controls both
|
- `FlowContainer` now has a convenience `spacing` parameter which controls both
|
||||||
`row_spacing` and `column_spacing` at the same time
|
`row_spacing` and `column_spacing` at the same time
|
||||||
|
|
||||||
deprecations:
|
deprecations:
|
||||||
|
|
||||||
- `rio.Fill` and `rio.FillLike` deprecated. Most components only support
|
- `rio.Fill` and `rio.FillLike` deprecated. Most components only support
|
||||||
specific fills, so these have no purpose any more
|
specific fills, so these have no purpose any more
|
||||||
- `display_controls` parameter of `CodeBlock` component renamed to
|
- `display_controls` parameter of `CodeBlock` component renamed to
|
||||||
`show_controls`
|
`show_controls`
|
||||||
|
|
||||||
breaking:
|
breaking:
|
||||||
|
|
||||||
- `Text.justify` now defaults to `"left"`
|
- `Text.justify` now defaults to `"left"`
|
||||||
- `FlowContainer.justify` now defaults to `"left"`
|
- `FlowContainer.justify` now defaults to `"left"`
|
||||||
- `rio.Theme` is no longer frozen, and can now be modified. This is breaking,
|
- `rio.Theme` is no longer frozen, and can now be modified. This is breaking,
|
||||||
because the `replace` method has been removed
|
because the `replace` method has been removed
|
||||||
|
|
||||||
## 0.8
|
## 0.8
|
||||||
|
|
||||||
- Rectangles now honor the theme's shadow color
|
- Rectangles now honor the theme's shadow color
|
||||||
- Renamed `Banner.markup` to `Banner.markdown`
|
- Renamed `Banner.markup` to `Banner.markdown`
|
||||||
- Removed the "multiline" style from Banners
|
- Removed the "multiline" style from Banners
|
||||||
- Removed `Button.initially_disabled_for`
|
- Removed `Button.initially_disabled_for`
|
||||||
- Added a `text_color` parameter to `Theme.from_colors` and
|
- Added a `text_color` parameter to `Theme.from_colors` and
|
||||||
`Theme.pair_from_colors`
|
`Theme.pair_from_colors`
|
||||||
- `rio run` now checks that the installed version of Rio is up-to-date
|
- `rio run` now checks that the installed version of Rio is up-to-date
|
||||||
|
|
||||||
## 0.7
|
## 0.7
|
||||||
|
|
||||||
- New example: multi-page website
|
- New example: multi-page website
|
||||||
- New component: CodeBlock
|
- New component: CodeBlock
|
||||||
- UserSettings can now have mutable default values
|
- UserSettings can now have mutable default values
|
||||||
- Removed "undefined space"
|
- Removed "undefined space"
|
||||||
|
|||||||
@@ -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
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
let reportKeyDown =
|
// Check if we need to update event listeners
|
||||||
this.keyDownCombinations === true ||
|
if (
|
||||||
this.keyDownCombinations.size > 0;
|
deltaState.reportKeyDown !== undefined ||
|
||||||
let reportKeyUp =
|
deltaState.reportKeyPress !== undefined ||
|
||||||
this.keyUpCombinations === true || this.keyUpCombinations.size > 0;
|
deltaState.event_order !== undefined
|
||||||
let reportKeyPress =
|
) {
|
||||||
this.keyPressCombinations === true ||
|
let reportKeyDown =
|
||||||
this.keyPressCombinations.size > 0;
|
this.keyDownCombinations === true ||
|
||||||
|
this.keyDownCombinations.size > 0;
|
||||||
|
let reportKeyPress =
|
||||||
|
this.keyPressCombinations === true ||
|
||||||
|
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,
|
||||||
this.handleKeyEvent(e, "KeyDown", this.keyDownCombinations);
|
eventOrder,
|
||||||
};
|
this.onKeyDownBound,
|
||||||
} else {
|
(e: KeyboardEvent) => {
|
||||||
this.element.onkeydown = null;
|
this.handleKeyEvent(
|
||||||
|
e,
|
||||||
|
"KeyPress",
|
||||||
|
this.keyPressCombinations
|
||||||
|
);
|
||||||
|
this.handleKeyEvent(e, "KeyDown", this.keyDownCombinations);
|
||||||
|
}
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (reportKeyUp) {
|
if (
|
||||||
this.element.onkeyup = (e: KeyboardEvent) => {
|
deltaState.reportKeyUp !== undefined ||
|
||||||
this.handleKeyEvent(e, "KeyUp", this.keyUpCombinations);
|
deltaState.event_order !== undefined
|
||||||
};
|
) {
|
||||||
} else {
|
let reportKeyUp =
|
||||||
this.element.onkeyup = null;
|
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.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