Add on progress typing to react player (#4211)

* add on progress typing to react player

* fix pyi file

* have the pyi here as well

* more pyi changes

* fix imports

* run pyi

* for some reason it want event on three lines no clue why

* simplify case for when type is in the same module

* run pyi

* remove last missing type for datadisplay
This commit is contained in:
Khaleel Al-Adhami 2024-10-22 13:09:14 -07:00 committed by GitHub
parent 227fb2cb75
commit a65fc2e90b
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
9 changed files with 65 additions and 36 deletions

View File

@ -109,19 +109,6 @@ class DataEditorTheme(Base):
text_medium: Optional[str] = None text_medium: Optional[str] = None
def on_edit_spec(pos, data: dict[str, Any]):
"""The on edit spec function.
Args:
pos: The position of the edit event.
data: The data of the edit event.
Returns:
The position and data.
"""
return [pos, data]
class Bounds(TypedDict): class Bounds(TypedDict):
"""The bounds of the group header.""" """The bounds of the group header."""
@ -149,7 +136,7 @@ class Rectangle(TypedDict):
class GridSelectionCurrent(TypedDict): class GridSelectionCurrent(TypedDict):
"""The current selection.""" """The current selection."""
cell: list[int] cell: tuple[int, int]
range: Rectangle range: Rectangle
rangeStack: list[Rectangle] rangeStack: list[Rectangle]
@ -167,7 +154,7 @@ class GroupHeaderClickedEventArgs(TypedDict):
kind: str kind: str
group: str group: str
location: list[int] location: tuple[int, int]
bounds: Bounds bounds: Bounds
isEdge: bool isEdge: bool
shiftKey: bool shiftKey: bool
@ -178,7 +165,7 @@ class GroupHeaderClickedEventArgs(TypedDict):
localEventY: int localEventY: int
button: int button: int
buttons: int buttons: int
scrollEdge: list[int] scrollEdge: tuple[int, int]
class GridCell(TypedDict): class GridCell(TypedDict):
@ -306,10 +293,10 @@ class DataEditor(NoSSRComponent):
on_cell_context_menu: EventHandler[identity_event(Tuple[int, int])] on_cell_context_menu: EventHandler[identity_event(Tuple[int, int])]
# Fired when a cell is edited. # Fired when a cell is edited.
on_cell_edited: EventHandler[on_edit_spec] on_cell_edited: EventHandler[identity_event(Tuple[int, int], GridCell)]
# Fired when a group header is clicked. # Fired when a group header is clicked.
on_group_header_clicked: EventHandler[on_edit_spec] on_group_header_clicked: EventHandler[identity_event(Tuple[int, int], GridCell)]
# Fired when a group header is right-clicked. # Fired when a group header is right-clicked.
on_group_header_context_menu: EventHandler[ on_group_header_context_menu: EventHandler[
@ -335,7 +322,9 @@ class DataEditor(NoSSRComponent):
on_delete: EventHandler[identity_event(GridSelection)] on_delete: EventHandler[identity_event(GridSelection)]
# Fired when editing is finished. # Fired when editing is finished.
on_finished_editing: EventHandler[identity_event(Union[GridCell, None], list[int])] on_finished_editing: EventHandler[
identity_event(Union[GridCell, None], tuple[int, int])
]
# Fired when a row is appended. # Fired when a row is appended.
on_row_appended: EventHandler[empty_event] on_row_appended: EventHandler[empty_event]

View File

@ -78,8 +78,6 @@ class DataEditorTheme(Base):
text_light: Optional[str] text_light: Optional[str]
text_medium: Optional[str] text_medium: Optional[str]
def on_edit_spec(pos, data: dict[str, Any]): ...
class Bounds(TypedDict): class Bounds(TypedDict):
x: int x: int
y: int y: int
@ -96,7 +94,7 @@ class Rectangle(TypedDict):
height: int height: int
class GridSelectionCurrent(TypedDict): class GridSelectionCurrent(TypedDict):
cell: list[int] cell: tuple[int, int]
range: Rectangle range: Rectangle
rangeStack: list[Rectangle] rangeStack: list[Rectangle]
@ -108,7 +106,7 @@ class GridSelection(TypedDict):
class GroupHeaderClickedEventArgs(TypedDict): class GroupHeaderClickedEventArgs(TypedDict):
kind: str kind: str
group: str group: str
location: list[int] location: tuple[int, int]
bounds: Bounds bounds: Bounds
isEdge: bool isEdge: bool
shiftKey: bool shiftKey: bool
@ -119,7 +117,7 @@ class GroupHeaderClickedEventArgs(TypedDict):
localEventY: int localEventY: int
button: int button: int
buttons: int buttons: int
scrollEdge: list[int] scrollEdge: tuple[int, int]
class GridCell(TypedDict): class GridCell(TypedDict):
span: Optional[List[int]] span: Optional[List[int]]
@ -189,17 +187,17 @@ class DataEditor(NoSSRComponent):
on_cell_activated: Optional[EventType[tuple[int, int]]] = None, on_cell_activated: Optional[EventType[tuple[int, int]]] = None,
on_cell_clicked: Optional[EventType[tuple[int, int]]] = None, on_cell_clicked: Optional[EventType[tuple[int, int]]] = None,
on_cell_context_menu: Optional[EventType[tuple[int, int]]] = None, on_cell_context_menu: Optional[EventType[tuple[int, int]]] = None,
on_cell_edited: Optional[EventType] = None, on_cell_edited: Optional[EventType[tuple[int, int], GridCell]] = None,
on_click: Optional[EventType[[]]] = None, on_click: Optional[EventType[[]]] = None,
on_column_resize: Optional[EventType[GridColumn, int]] = None, on_column_resize: Optional[EventType[GridColumn, int]] = None,
on_context_menu: Optional[EventType[[]]] = None, on_context_menu: Optional[EventType[[]]] = None,
on_delete: Optional[EventType[GridSelection]] = None, on_delete: Optional[EventType[GridSelection]] = None,
on_double_click: Optional[EventType[[]]] = None, on_double_click: Optional[EventType[[]]] = None,
on_finished_editing: Optional[ on_finished_editing: Optional[
EventType[Union[GridCell, None], list[int]] EventType[Union[GridCell, None], tuple[int, int]]
] = None, ] = None,
on_focus: Optional[EventType[[]]] = None, on_focus: Optional[EventType[[]]] = None,
on_group_header_clicked: Optional[EventType] = None, on_group_header_clicked: Optional[EventType[tuple[int, int], GridCell]] = None,
on_group_header_context_menu: Optional[ on_group_header_context_menu: Optional[
EventType[int, GroupHeaderClickedEventArgs] EventType[int, GroupHeaderClickedEventArgs]
] = None, ] = None,

View File

@ -5,7 +5,9 @@
# ------------------------------------------------------ # ------------------------------------------------------
from typing import Any, Dict, Literal, Optional, Union, overload from typing import Any, Dict, Literal, Optional, Union, overload
from reflex.event import EventType from reflex.event import (
EventType,
)
from reflex.style import Style from reflex.style import Style
from reflex.vars.base import Var from reflex.vars.base import Var

View File

@ -1,5 +1,6 @@
"""React Player component for audio and video.""" """React Player component for audio and video."""
from . import react_player
from .audio import Audio from .audio import Audio
from .video import Video from .video import Video

View File

@ -5,6 +5,7 @@
# ------------------------------------------------------ # ------------------------------------------------------
from typing import Any, Dict, Optional, Union, overload from typing import Any, Dict, Optional, Union, overload
import reflex
from reflex.components.react_player.react_player import ReactPlayer from reflex.components.react_player.react_player import ReactPlayer
from reflex.event import EventType from reflex.event import EventType
from reflex.style import Style from reflex.style import Style
@ -58,7 +59,9 @@ class Audio(ReactPlayer):
on_play: Optional[EventType[[]]] = None, on_play: Optional[EventType[[]]] = None,
on_playback_quality_change: Optional[EventType[[]]] = None, on_playback_quality_change: Optional[EventType[[]]] = None,
on_playback_rate_change: Optional[EventType[[]]] = None, on_playback_rate_change: Optional[EventType[[]]] = None,
on_progress: Optional[EventType] = None, on_progress: Optional[
EventType[reflex.components.react_player.react_player.Progress]
] = None,
on_ready: Optional[EventType[[]]] = None, on_ready: Optional[EventType[[]]] = None,
on_scroll: Optional[EventType[[]]] = None, on_scroll: Optional[EventType[[]]] = None,
on_seek: Optional[EventType[float]] = None, on_seek: Optional[EventType[float]] = None,

View File

@ -2,11 +2,22 @@
from __future__ import annotations from __future__ import annotations
from typing_extensions import TypedDict
from reflex.components.component import NoSSRComponent from reflex.components.component import NoSSRComponent
from reflex.event import EventHandler, empty_event, identity_event from reflex.event import EventHandler, empty_event, identity_event
from reflex.vars.base import Var from reflex.vars.base import Var
class Progress(TypedDict):
"""Callback containing played and loaded progress as a fraction, and playedSeconds and loadedSeconds in seconds."""
played: float
playedSeconds: float
loaded: float
loadedSeconds: float
class ReactPlayer(NoSSRComponent): class ReactPlayer(NoSSRComponent):
"""Using react-player and not implement all props and callback yet. """Using react-player and not implement all props and callback yet.
reference: https://github.com/cookpete/react-player. reference: https://github.com/cookpete/react-player.
@ -55,7 +66,7 @@ class ReactPlayer(NoSSRComponent):
on_play: EventHandler[empty_event] on_play: EventHandler[empty_event]
# Callback containing played and loaded progress as a fraction, and playedSeconds and loadedSeconds in seconds. eg { played: 0.12, playedSeconds: 11.3, loaded: 0.34, loadedSeconds: 16.7 } # Callback containing played and loaded progress as a fraction, and playedSeconds and loadedSeconds in seconds. eg { played: 0.12, playedSeconds: 11.3, loaded: 0.34, loadedSeconds: 16.7 }
on_progress: EventHandler[lambda progress: [progress]] on_progress: EventHandler[identity_event(Progress)]
# Callback containing duration of the media, in seconds. # Callback containing duration of the media, in seconds.
on_duration: EventHandler[identity_event(float)] on_duration: EventHandler[identity_event(float)]

View File

@ -5,11 +5,19 @@
# ------------------------------------------------------ # ------------------------------------------------------
from typing import Any, Dict, Optional, Union, overload from typing import Any, Dict, Optional, Union, overload
from typing_extensions import TypedDict
from reflex.components.component import NoSSRComponent from reflex.components.component import NoSSRComponent
from reflex.event import EventType from reflex.event import EventType
from reflex.style import Style from reflex.style import Style
from reflex.vars.base import Var from reflex.vars.base import Var
class Progress(TypedDict):
played: float
playedSeconds: float
loaded: float
loadedSeconds: float
class ReactPlayer(NoSSRComponent): class ReactPlayer(NoSSRComponent):
@overload @overload
@classmethod @classmethod
@ -56,7 +64,7 @@ class ReactPlayer(NoSSRComponent):
on_play: Optional[EventType[[]]] = None, on_play: Optional[EventType[[]]] = None,
on_playback_quality_change: Optional[EventType[[]]] = None, on_playback_quality_change: Optional[EventType[[]]] = None,
on_playback_rate_change: Optional[EventType[[]]] = None, on_playback_rate_change: Optional[EventType[[]]] = None,
on_progress: Optional[EventType] = None, on_progress: Optional[EventType[Progress]] = None,
on_ready: Optional[EventType[[]]] = None, on_ready: Optional[EventType[[]]] = None,
on_scroll: Optional[EventType[[]]] = None, on_scroll: Optional[EventType[[]]] = None,
on_seek: Optional[EventType[float]] = None, on_seek: Optional[EventType[float]] = None,

View File

@ -5,6 +5,7 @@
# ------------------------------------------------------ # ------------------------------------------------------
from typing import Any, Dict, Optional, Union, overload from typing import Any, Dict, Optional, Union, overload
import reflex
from reflex.components.react_player.react_player import ReactPlayer from reflex.components.react_player.react_player import ReactPlayer
from reflex.event import EventType from reflex.event import EventType
from reflex.style import Style from reflex.style import Style
@ -58,7 +59,9 @@ class Video(ReactPlayer):
on_play: Optional[EventType[[]]] = None, on_play: Optional[EventType[[]]] = None,
on_playback_quality_change: Optional[EventType[[]]] = None, on_playback_quality_change: Optional[EventType[[]]] = None,
on_playback_rate_change: Optional[EventType[[]]] = None, on_playback_rate_change: Optional[EventType[[]]] = None,
on_progress: Optional[EventType] = None, on_progress: Optional[
EventType[reflex.components.react_player.react_player.Progress]
] = None,
on_ready: Optional[EventType[[]]] = None, on_ready: Optional[EventType[[]]] = None,
on_scroll: Optional[EventType[[]]] = None, on_scroll: Optional[EventType[[]]] = None,
on_seek: Optional[EventType[float]] = None, on_seek: Optional[EventType[float]] = None,

View File

@ -214,7 +214,9 @@ def _get_type_hint(value, type_hint_globals, is_optional=True) -> str:
return res return res
def _generate_imports(typing_imports: Iterable[str]) -> list[ast.ImportFrom]: def _generate_imports(
typing_imports: Iterable[str],
) -> list[ast.ImportFrom | ast.Import]:
"""Generate the import statements for the stub file. """Generate the import statements for the stub file.
Args: Args:
@ -228,6 +230,7 @@ def _generate_imports(typing_imports: Iterable[str]) -> list[ast.ImportFrom]:
ast.ImportFrom(module=name, names=[ast.alias(name=val) for val in values]) ast.ImportFrom(module=name, names=[ast.alias(name=val) for val in values])
for name, values in DEFAULT_IMPORTS.items() for name, values in DEFAULT_IMPORTS.items()
], ],
ast.Import([ast.alias("reflex")]),
] ]
@ -372,12 +375,13 @@ def _extract_class_props_as_ast_nodes(
return kwargs return kwargs
def type_to_ast(typ) -> ast.AST: def type_to_ast(typ, cls: type) -> ast.AST:
"""Converts any type annotation into its AST representation. """Converts any type annotation into its AST representation.
Handles nested generic types, unions, etc. Handles nested generic types, unions, etc.
Args: Args:
typ: The type annotation to convert. typ: The type annotation to convert.
cls: The class where the type annotation is used.
Returns: Returns:
The AST representation of the type annotation. The AST representation of the type annotation.
@ -390,6 +394,16 @@ def type_to_ast(typ) -> ast.AST:
# Handle plain types (int, str, custom classes, etc.) # Handle plain types (int, str, custom classes, etc.)
if origin is None: if origin is None:
if hasattr(typ, "__name__"): if hasattr(typ, "__name__"):
if typ.__module__.startswith("reflex."):
typ_parts = typ.__module__.split(".")
cls_parts = cls.__module__.split(".")
zipped = list(zip(typ_parts, cls_parts, strict=False))
if all(a == b for a, b in zipped) and len(typ_parts) == len(cls_parts):
return ast.Name(id=typ.__name__)
return ast.Name(id=typ.__module__ + "." + typ.__name__)
return ast.Name(id=typ.__name__) return ast.Name(id=typ.__name__)
elif hasattr(typ, "_name"): elif hasattr(typ, "_name"):
return ast.Name(id=typ._name) return ast.Name(id=typ._name)
@ -406,7 +420,7 @@ def type_to_ast(typ) -> ast.AST:
return ast.Name(id=base_name) return ast.Name(id=base_name)
# Convert all type arguments recursively # Convert all type arguments recursively
arg_nodes = [type_to_ast(arg) for arg in args] arg_nodes = [type_to_ast(arg, cls) for arg in args]
# Special case for single-argument types (like List[T] or Optional[T]) # Special case for single-argument types (like List[T] or Optional[T])
if len(arg_nodes) == 1: if len(arg_nodes) == 1:
@ -487,7 +501,7 @@ def _generate_component_create_functiondef(
] ]
# Convert each argument type to its AST representation # Convert each argument type to its AST representation
type_args = [type_to_ast(arg) for arg in arguments_without_var] type_args = [type_to_ast(arg, cls=clz) for arg in arguments_without_var]
# Join the type arguments with commas for EventType # Join the type arguments with commas for EventType
args_str = ", ".join(ast.unparse(arg) for arg in type_args) args_str = ", ".join(ast.unparse(arg) for arg in type_args)