Merge remote-tracking branch 'origin/main' into reflex-0.4.0

This commit is contained in:
Masen Furer 2024-02-07 13:53:39 -08:00
commit c124650082
No known key found for this signature in database
GPG Key ID: 2AE2BD5531FF94F4
27 changed files with 234 additions and 212 deletions

View File

@ -460,7 +460,12 @@ def remove_tailwind_from_postcss() -> tuple[str, str]:
def purge_web_pages_dir():
"""Empty out .web directory."""
"""Empty out .web/pages directory."""
if _is_dev_mode() and os.environ.get("REFLEX_PERSIST_WEB_DIR"):
# Skip purging the web directory in dev mode if REFLEX_PERSIST_WEB_DIR is set.
return
# Empty out the web pages directory.
utils.empty_dir(constants.Dirs.WEB_PAGES, keep_files=["_app.js"])

View File

@ -15,7 +15,6 @@ class ChakraComponent(Component):
library = "@chakra-ui/react@2.6.1"
lib_dependencies: List[str] = [
"@chakra-ui/system@2.5.7",
"focus-visible@5.2.0",
"framer-motion@10.16.4",
]
@ -26,25 +25,6 @@ class ChakraComponent(Component):
(60, "ChakraProvider"): chakra_provider,
}
def get_imports(self) -> imports.ImportDict:
"""Chakra requires focus-visible and imported into each page.
This allows the GlobalStyle defined by the ChakraProvider to hide the blue border.
Returns:
The imports for the component.
"""
return imports.merge_imports(
super().get_imports(),
{
"": {
imports.ImportVar(
tag="focus-visible/dist/focus-visible", install=False
)
}
},
)
def _get_style(self) -> dict:
"""Get the style for the component.
@ -65,25 +45,11 @@ class ChakraComponent(Component):
dep: [imports.ImportVar(tag=None, render=False)]
for dep in [
"@chakra-ui/system@2.5.7",
"focus-visible@5.2.0",
"framer-motion@10.16.4",
]
}
class Global(Component):
"""The emotion/react Global styling component."""
library = "@emotion/react@^11.11.0"
lib_dependencies: List[str] = [
"@emotion/styled@^11.11.0",
]
tag = "Global"
styles: Var[str]
class ChakraProvider(ChakraComponent):
"""Top level Chakra provider must be included in any app using chakra components."""
@ -99,7 +65,6 @@ class ChakraProvider(ChakraComponent):
A new ChakraProvider component.
"""
return super().create(
Global.create(styles=Var.create("GlobalStyles", _var_is_local=False)),
theme=Var.create("extendTheme(theme)", _var_is_local=False),
)
@ -111,22 +76,8 @@ class ChakraProvider(ChakraComponent):
_imports.setdefault("/utils/theme.js", []).append(
imports.ImportVar(tag="theme", is_default=True),
)
_imports.setdefault(Global.__fields__["library"].default, []).append(
imports.ImportVar(tag="css", is_default=False),
)
return _imports
def _get_custom_code(self) -> str | None:
return """
const GlobalStyles = css`
/* Hide the blue border around Chakra components. */
.js-focus-visible :focus:not([data-focus-visible-added]) {
outline: none;
box-shadow: none;
}
`;
"""
@staticmethod
@lru_cache(maxsize=None)
def _get_app_wrap_components() -> dict[tuple[int, str], Component]:

View File

@ -14,7 +14,6 @@ from reflex.utils import imports
from reflex.vars import Var
class ChakraComponent(Component):
def get_imports(self) -> imports.ImportDict: ...
@overload
@classmethod
def create( # type: ignore
@ -95,88 +94,6 @@ class ChakraComponent(Component):
"""
...
class Global(Component):
@overload
@classmethod
def create( # type: ignore
cls,
*children,
styles: Optional[Union[Var[str], str]] = None,
style: Optional[Style] = None,
key: Optional[Any] = None,
id: Optional[Any] = None,
class_name: Optional[Any] = None,
autofocus: Optional[bool] = None,
_rename_props: Optional[Dict[str, str]] = None,
custom_attrs: Optional[Dict[str, Union[Var, str]]] = None,
on_blur: Optional[
Union[EventHandler, EventSpec, list, function, BaseVar]
] = None,
on_click: Optional[
Union[EventHandler, EventSpec, list, function, BaseVar]
] = None,
on_context_menu: Optional[
Union[EventHandler, EventSpec, list, function, BaseVar]
] = None,
on_double_click: Optional[
Union[EventHandler, EventSpec, list, function, BaseVar]
] = None,
on_focus: Optional[
Union[EventHandler, EventSpec, list, function, BaseVar]
] = None,
on_mount: Optional[
Union[EventHandler, EventSpec, list, function, BaseVar]
] = None,
on_mouse_down: Optional[
Union[EventHandler, EventSpec, list, function, BaseVar]
] = None,
on_mouse_enter: Optional[
Union[EventHandler, EventSpec, list, function, BaseVar]
] = None,
on_mouse_leave: Optional[
Union[EventHandler, EventSpec, list, function, BaseVar]
] = None,
on_mouse_move: Optional[
Union[EventHandler, EventSpec, list, function, BaseVar]
] = None,
on_mouse_out: Optional[
Union[EventHandler, EventSpec, list, function, BaseVar]
] = None,
on_mouse_over: Optional[
Union[EventHandler, EventSpec, list, function, BaseVar]
] = None,
on_mouse_up: Optional[
Union[EventHandler, EventSpec, list, function, BaseVar]
] = None,
on_scroll: Optional[
Union[EventHandler, EventSpec, list, function, BaseVar]
] = None,
on_unmount: Optional[
Union[EventHandler, EventSpec, list, function, BaseVar]
] = None,
**props
) -> "Global":
"""Create the component.
Args:
*children: The children of the component.
style: The style of the component.
key: A unique key for the component.
id: The id for the component.
class_name: The class name for the component.
autofocus: Whether the component should take the focus once the page is loaded
_rename_props: props to change the name of
custom_attrs: custom attribute
**props: The props of the component.
Returns:
The component.
Raises:
TypeError: If an invalid child is passed.
"""
...
class ChakraProvider(ChakraComponent):
@overload
@classmethod

View File

@ -91,15 +91,9 @@ class Input(ChakraComponent):
Returns:
The component.
"""
if (
isinstance(props.get("value"), Var) and props.get("on_change")
) or "debounce_timeout" in props:
# Currently default to 50ms, which appears to be a good balance
debounce_timeout = props.pop("debounce_timeout", 50)
if props.get("value") is not None and props.get("on_change"):
# create a debounced input if the user requests full control to avoid typing jank
return DebounceInput.create(
super().create(*children, **props), debounce_timeout=debounce_timeout
)
return DebounceInput.create(super().create(*children, **props))
return super().create(*children, **props)

View File

@ -74,13 +74,7 @@ class TextArea(ChakraComponent):
Returns:
The component.
"""
if (
isinstance(props.get("value"), Var) and props.get("on_change")
) or "debounce_timeout" in props:
# Currently default to 50ms, which appears to be a good balance
debounce_timeout = props.pop("debounce_timeout", 50)
if props.get("value") is not None and props.get("on_change"):
# create a debounced input if the user requests full control to avoid typing jank
return DebounceInput.create(
super().create(*children, **props), debounce_timeout=debounce_timeout
)
return DebounceInput.create(super().create(*children, **props))
return super().create(*children, **props)

View File

@ -7,6 +7,8 @@ from reflex.components.component import Component
from reflex.constants import EventTriggers
from reflex.vars import Var, VarData
DEFAULT_DEBOUNCE_TIMEOUT = 300
class DebounceInput(Component):
"""The DebounceInput component is used to buffer input events on the client side.
@ -23,7 +25,7 @@ class DebounceInput(Component):
min_length: Var[int]
# Time to wait between end of input and triggering on_change
debounce_timeout: Var[int]
debounce_timeout: Var[int] = DEFAULT_DEBOUNCE_TIMEOUT # type: ignore
# If true, notify when Enter key is pressed
force_notify_by_enter: Var[bool]

View File

@ -12,6 +12,8 @@ from reflex.components.component import Component
from reflex.constants import EventTriggers
from reflex.vars import Var, VarData
DEFAULT_DEBOUNCE_TIMEOUT = 300
class DebounceInput(Component):
@overload
@classmethod

View File

@ -3,7 +3,7 @@
from __future__ import annotations
from types import SimpleNamespace
from typing import Any, Dict, Literal
from typing import Any, Dict, List, Literal
from reflex.components.component import Component
from reflex.components.core import match
@ -344,6 +344,8 @@ class AccordionRoot(AccordionComponent):
# The var_data associated with the component.
_var_data: VarData = VarData() # type: ignore
_valid_children: List[str] = ["AccordionItem"]
@classmethod
def create(cls, *children, **props) -> Component:
"""Create the Accordion root component.
@ -451,6 +453,14 @@ class AccordionItem(AccordionComponent):
# When true, prevents the user from interacting with the item.
disabled: Var[bool]
_valid_children: List[str] = [
"AccordionHeader",
"AccordionTrigger",
"AccordionContent",
]
_valid_parents: List[str] = ["AccordionRoot"]
def _apply_theme(self, theme: Component):
self.style = Style(
{

View File

@ -8,7 +8,7 @@ from reflex.vars import Var, BaseVar, ComputedVar
from reflex.event import EventChain, EventHandler, EventSpec
from reflex.style import Style
from types import SimpleNamespace
from typing import Any, Dict, Literal
from typing import Any, Dict, List, Literal
from reflex.components.component import Component
from reflex.components.core import match
from reflex.components.lucide.icon import Icon

View File

@ -1,6 +1,6 @@
"""Interactive components provided by @radix-ui/themes."""
from types import SimpleNamespace
from typing import Any, Dict, Literal
from typing import Any, Dict, List, Literal
from reflex.constants import EventTriggers
from reflex.vars import Var
@ -19,6 +19,8 @@ class ContextMenuRoot(RadixThemesComponent):
# The modality of the context menu. When set to true, interaction with outside elements will be disabled and only menu content will be visible to screen readers.
modal: Var[bool]
_invalid_children: List[str] = ["ContextMenuItem"]
def get_event_triggers(self) -> Dict[str, Any]:
"""Get the events triggers signatures for the component.
@ -39,6 +41,10 @@ class ContextMenuTrigger(RadixThemesComponent):
# Whether the trigger is disabled
disabled: Var[bool]
_valid_parents: List[str] = ["ContextMenuRoot"]
_invalid_children: List[str] = ["ContextMenuContent"]
class ContextMenuContent(RadixThemesComponent):
"""Trigger an action or event, such as submitting a form or displaying a dialog."""
@ -60,7 +66,7 @@ class ContextMenuContent(RadixThemesComponent):
# The vertical distance in pixels from the anchor.
align_offset: Var[int]
# When true, overrides the side andalign preferences to prevent collisions with boundary edges.
# When true, overrides the side and aligns preferences to prevent collisions with boundary edges.
avoid_collisions: Var[bool]
def get_event_triggers(self) -> Dict[str, Any]:
@ -93,6 +99,8 @@ class ContextMenuSubTrigger(RadixThemesComponent):
# Whether the trigger is disabled
disabled: Var[bool]
_valid_parents: List[str] = ["ContextMenuContent", "ContextMenuSub"]
class ContextMenuSubContent(RadixThemesComponent):
"""Trigger an action or event, such as submitting a form or displaying a dialog."""
@ -102,6 +110,8 @@ class ContextMenuSubContent(RadixThemesComponent):
# When true, keyboard navigation will loop from last item to first, and vice versa.
loop: Var[bool]
_valid_parents: List[str] = ["ContextMenuSub"]
def get_event_triggers(self) -> Dict[str, Any]:
"""Get the events triggers signatures for the component.
@ -128,6 +138,8 @@ class ContextMenuItem(RadixThemesComponent):
# Shortcut to render a menu item as a link
shortcut: Var[str]
_valid_parents: List[str] = ["ContextMenuContent", "ContextMenuSubContent"]
class ContextMenuSeparator(RadixThemesComponent):
"""Trigger an action or event, such as submitting a form or displaying a dialog."""

View File

@ -8,7 +8,7 @@ from reflex.vars import Var, BaseVar, ComputedVar
from reflex.event import EventChain, EventHandler, EventSpec
from reflex.style import Style
from types import SimpleNamespace
from typing import Any, Dict, Literal
from typing import Any, Dict, List, Literal
from reflex.constants import EventTriggers
from reflex.vars import Var
from ..base import LiteralAccentColor, RadixThemesComponent
@ -472,7 +472,7 @@ class ContextMenuContent(RadixThemesComponent):
variant: Variant of button: "solid" | "soft" | "outline" | "ghost"
high_contrast: Whether to render the button with higher contrast color against background
align_offset: The vertical distance in pixels from the anchor.
avoid_collisions: When true, overrides the side andalign preferences to prevent collisions with boundary edges.
avoid_collisions: When true, overrides the side and aligns preferences to prevent collisions with boundary edges.
style: The style of the component.
key: A unique key for the component.
id: The id for the component.

View File

@ -1,6 +1,6 @@
"""Interactive components provided by @radix-ui/themes."""
from types import SimpleNamespace
from typing import Any, Dict, Literal, Union
from typing import Any, Dict, List, Literal, Union
from reflex.constants import EventTriggers
from reflex.vars import Var
@ -44,6 +44,8 @@ class DropdownMenuRoot(RadixThemesComponent):
# The reading direction of submenus when applicable. If omitted, inherits globally from DirectionProvider or assumes LTR (left-to-right) reading mode.
dir: Var[LiteralDirType]
_invalid_children: List[str] = ["DropdownMenuItem"]
def get_event_triggers(self) -> Dict[str, Any]:
"""Get the events triggers signatures for the component.
@ -64,6 +66,10 @@ class DropdownMenuTrigger(RadixThemesComponent):
# Change the default rendered element for the one passed as a child, merging their props and behavior. Defaults to False.
as_child: Var[bool]
_valid_parents: List[str] = ["DropdownMenuRoot"]
_invalid_children: List[str] = ["DropdownMenuContent"]
class DropdownMenuContent(RadixThemesComponent):
"""The Dropdown Menu Content component that pops out when the dropdown menu is open."""
@ -148,6 +154,8 @@ class DropdownMenuSubTrigger(RadixThemesComponent):
# Optional text used for typeahead purposes. By default the typeahead behavior will use the .textContent of the item. Use this when the content is complex, or you have non-textual content inside.
text_value: Var[str]
_valid_parents: List[str] = ["DropdownMenuContent", "DropdownMenuSub"]
class DropdownMenuSub(RadixThemesComponent):
"""Contains all the parts of a submenu."""
@ -219,6 +227,8 @@ class DropdownMenuSubContent(RadixThemesComponent):
# Whether to hide the content when the trigger becomes fully occluded. Defaults to False.
hide_when_detached: Var[bool]
_valid_parents: List[str] = ["DropdownMenuSub"]
def get_event_triggers(self) -> Dict[str, Any]:
"""Get the events triggers signatures for the component.
@ -254,6 +264,8 @@ class DropdownMenuItem(RadixThemesComponent):
# Optional text used for typeahead purposes. By default the typeahead behavior will use the .textContent of the item. Use this when the content is complex, or you have non-textual content inside.
text_value: Var[str]
_valid_parents: List[str] = ["DropdownMenuContent", "DropdownMenuSubContent"]
def get_event_triggers(self) -> Dict[str, Any]:
"""Get the events triggers signatures for the component.

View File

@ -8,7 +8,7 @@ from reflex.vars import Var, BaseVar, ComputedVar
from reflex.event import EventChain, EventHandler, EventSpec
from reflex.style import Style
from types import SimpleNamespace
from typing import Any, Dict, Literal, Union
from typing import Any, Dict, List, Literal, Union
from reflex.constants import EventTriggers
from reflex.vars import Var
from ..base import LiteralAccentColor, RadixThemesComponent

View File

@ -78,6 +78,8 @@ class SelectTrigger(RadixThemesComponent):
# The placeholder of the select trigger
placeholder: Var[str]
_valid_parents: List[str] = ["SelectRoot"]
class SelectContent(RadixThemesComponent):
"""The component that pops out when the select is open."""
@ -127,6 +129,8 @@ class SelectGroup(RadixThemesComponent):
tag = "Select.Group"
_valid_parents: List[str] = ["SelectContent"]
class SelectItem(RadixThemesComponent):
"""The component that contains the select items."""
@ -139,12 +143,16 @@ class SelectItem(RadixThemesComponent):
# Whether the select item is disabled
disabled: Var[bool]
_valid_parents: List[str] = ["SelectGroup", "SelectContent"]
class SelectLabel(RadixThemesComponent):
"""Used to render the label of a group, it isn't focusable using arrow keys."""
tag = "Select.Label"
_valid_parents: List[str] = ["SelectGroup"]
class SelectSeparator(RadixThemesComponent):
"""Used to visually separate items in the Select."""

View File

@ -1,6 +1,7 @@
"""Interactive components provided by @radix-ui/themes."""
from typing import Any, Dict, List, Literal, Union
from typing import Any, Dict, List, Literal, Optional, Union
from reflex.components.component import Component
from reflex.constants import EventTriggers
from reflex.vars import Var
@ -35,7 +36,7 @@ class Slider(RadixThemesComponent):
radius: Var[LiteralRadius]
# The value of the slider when initially rendered. Use when you do not need to control the state of the slider.
default_value: Var[List[Union[float, int]]]
default_value: Var[Union[List[Union[float, int]], float, int]]
# The controlled value of the slider. Must be used in conjunction with onValueChange.
value: Var[List[Union[float, int]]]
@ -73,5 +74,39 @@ class Slider(RadixThemesComponent):
EventTriggers.ON_VALUE_COMMIT: lambda e0: [e0],
}
@classmethod
def create(
cls,
*children,
width: Optional[str] = "100%",
**props,
) -> Component:
"""Create a Slider component.
slider = Slider.create
Args:
*children: The children of the component.
width: The width of the slider.
**props: The properties of the component.
Returns:
The component.
"""
default_value = props.pop("default_value", [50])
if isinstance(default_value, Var):
if issubclass(default_value._var_type, (int, float)):
default_value = [default_value]
elif isinstance(default_value, (int, float)):
default_value = [default_value]
style = props.setdefault("style", {})
style.update(
{
"width": width,
}
)
return super().create(*children, default_value=default_value, **props)
slider = Slider.create

View File

@ -7,7 +7,8 @@ from typing import Any, Dict, Literal, Optional, Union, overload
from reflex.vars import Var, BaseVar, ComputedVar
from reflex.event import EventChain, EventHandler, EventSpec
from reflex.style import Style
from typing import Any, Dict, List, Literal, Union
from typing import Any, Dict, List, Literal, Optional, Union
from reflex.components.component import Component
from reflex.constants import EventTriggers
from reflex.vars import Var
from ..base import LiteralAccentColor, LiteralRadius, RadixThemesComponent
@ -19,7 +20,17 @@ class Slider(RadixThemesComponent):
def create( # type: ignore
cls,
*children,
color: Optional[Union[Var[str], str]] = None,
width: Optional[str] = "100%",
as_child: Optional[Union[Var[bool], bool]] = None,
size: Optional[
Union[Var[Literal["1", "2", "3"]], Literal["1", "2", "3"]]
] = None,
variant: Optional[
Union[
Var[Literal["classic", "surface", "soft"]],
Literal["classic", "surface", "soft"],
]
] = None,
color_scheme: Optional[
Union[
Var[
@ -82,16 +93,6 @@ class Slider(RadixThemesComponent):
],
]
] = None,
as_child: Optional[Union[Var[bool], bool]] = None,
size: Optional[
Union[Var[Literal["1", "2", "3"]], Literal["1", "2", "3"]]
] = None,
variant: Optional[
Union[
Var[Literal["classic", "surface", "soft"]],
Literal["classic", "surface", "soft"],
]
] = None,
high_contrast: Optional[Union[Var[bool], bool]] = None,
radius: Optional[
Union[
@ -100,7 +101,10 @@ class Slider(RadixThemesComponent):
]
] = None,
default_value: Optional[
Union[Var[List[Union[float, int]]], List[Union[float, int]]]
Union[
Var[Union[List[Union[float, int]], float, int]],
Union[List[Union[float, int]], float, int],
]
] = None,
value: Optional[
Union[Var[List[Union[float, int]]], List[Union[float, int]]]
@ -176,18 +180,15 @@ class Slider(RadixThemesComponent):
] = None,
**props
) -> "Slider":
"""Create a new component instance.
Will prepend "RadixThemes" to the component tag to avoid conflicts with
other UI libraries for common names, like Text and Button.
"""Create a Slider component.
Args:
*children: Child components.
color: map to CSS default color property.
color_scheme: map to radix color property.
*children: The children of the component.
width: The width of the slider.
as_child: Change the default rendered element for the one passed as a child, merging their props and behavior.
size: Button size "1" - "3"
variant: Variant of button
color_scheme: Override theme color for button
high_contrast: Whether to render the button with higher contrast color against background
radius: Override theme radius for button: "none" | "small" | "medium" | "large" | "full"
default_value: The value of the slider when initially rendered. Use when you do not need to control the state of the slider.
@ -205,10 +206,10 @@ class Slider(RadixThemesComponent):
autofocus: Whether the component should take the focus once the page is loaded
_rename_props: props to change the name of
custom_attrs: custom attribute
**props: Component properties.
**props: The properties of the component.
Returns:
A new component instance.
The component.
"""
...

View File

@ -1,6 +1,6 @@
"""Interactive components provided by @radix-ui/themes."""
from types import SimpleNamespace
from typing import Literal, Union
from typing import List, Literal, Union
from reflex import el
from reflex.vars import Var
@ -27,6 +27,10 @@ class TableHeader(el.Thead, RadixThemesComponent):
tag = "Table.Header"
_invalid_children: List[str] = ["TableBody"]
_valid_parents: List[str] = ["TableRoot"]
class TableRow(el.Tr, RadixThemesComponent):
"""A row containing table cells."""
@ -36,6 +40,8 @@ class TableRow(el.Tr, RadixThemesComponent):
# The alignment of the row
align: Var[Literal["start", "center", "end", "baseline"]]
_invalid_children: List[str] = ["TableBody", "TableHeader", "TableRow"]
class TableColumnHeaderCell(el.Th, RadixThemesComponent):
"""A table cell that is semantically treated as a column header."""
@ -48,12 +54,30 @@ class TableColumnHeaderCell(el.Th, RadixThemesComponent):
# width of the column
width: Var[Union[str, int]]
_invalid_children: List[str] = [
"TableBody",
"TableHeader",
"TableRow",
"TableCell",
"TableColumnHeaderCell",
"TableRowHeaderCell",
]
class TableBody(el.Tbody, RadixThemesComponent):
"""The body of the table contains the data rows."""
tag = "Table.Body"
_invalid_children: List[str] = [
"TableHeader",
"TableRowHeaderCell",
"TableColumnHeaderCell",
"TableCell",
]
_valid_parents: List[str] = ["TableRoot"]
class TableCell(el.Td, RadixThemesComponent):
"""A cell containing data."""
@ -66,6 +90,14 @@ class TableCell(el.Td, RadixThemesComponent):
# width of the column
width: Var[Union[str, int]]
_invalid_children: List[str] = [
"TableBody",
"TableHeader",
"TableRowHeaderCell",
"TableColumnHeaderCell",
"TableCell",
]
class TableRowHeaderCell(el.Th, RadixThemesComponent):
"""A table cell that is semantically treated as a row header."""
@ -78,6 +110,15 @@ class TableRowHeaderCell(el.Th, RadixThemesComponent):
# width of the column
width: Var[Union[str, int]]
_invalid_children: List[str] = [
"TableBody",
"TableHeader",
"TableRow",
"TableCell",
"TableColumnHeaderCell",
"TableRowHeaderCell",
]
class Table(SimpleNamespace):
"""Table components namespace."""

View File

@ -8,7 +8,7 @@ from reflex.vars import Var, BaseVar, ComputedVar
from reflex.event import EventChain, EventHandler, EventSpec
from reflex.style import Style
from types import SimpleNamespace
from typing import Literal, Union
from typing import List, Literal, Union
from reflex import el
from reflex.vars import Var
from ..base import RadixThemesComponent

View File

@ -1,6 +1,6 @@
"""Interactive components provided by @radix-ui/themes."""
from types import SimpleNamespace
from typing import Any, Dict, Literal
from typing import Any, Dict, List, Literal
from reflex.constants import EventTriggers
from reflex.vars import Var
@ -59,6 +59,8 @@ class TabsTrigger(RadixThemesComponent):
# Whether the tab is disabled
disabled: Var[bool]
_valid_parents: List[str] = ["TabsList"]
class TabsContent(RadixThemesComponent):
"""Trigger an action or event, such as submitting a form or displaying a dialog."""

View File

@ -8,7 +8,7 @@ from reflex.vars import Var, BaseVar, ComputedVar
from reflex.event import EventChain, EventHandler, EventSpec
from reflex.style import Style
from types import SimpleNamespace
from typing import Any, Dict, Literal
from typing import Any, Dict, List, Literal
from reflex.constants import EventTriggers
from reflex.vars import Var
from ..base import RadixThemesComponent

View File

@ -40,15 +40,9 @@ class TextArea(RadixThemesComponent, el.Textarea):
Returns:
The component.
"""
if (
isinstance(props.get("value"), Var) and props.get("on_change")
) or "debounce_timeout" in props:
# Currently default to 50ms, which appears to be a good balance
debounce_timeout = props.pop("debounce_timeout", 50)
if props.get("value") is not None and props.get("on_change"):
# create a debounced input if the user requests full control to avoid typing jank
return DebounceInput.create(
super().create(*children, **props), debounce_timeout=debounce_timeout
)
return DebounceInput.create(super().create(*children, **props))
return super().create(*children, **props)
def get_event_triggers(self) -> Dict[str, Any]:

View File

@ -56,15 +56,9 @@ class TextFieldInput(el.Input, TextFieldRoot):
Returns:
The component.
"""
if (
isinstance(props.get("value"), Var) and props.get("on_change")
) or "debounce_timeout" in props:
# Currently default to 50ms, which appears to be a good balance
debounce_timeout = props.pop("debounce_timeout", 50)
if props.get("value") is not None and props.get("on_change"):
# create a debounced input if the user requests full control to avoid typing jank
return DebounceInput.create(
super().create(*children, **props), debounce_timeout=debounce_timeout
)
return DebounceInput.create(super().create(*children, **props))
return super().create(*children, **props)
def get_event_triggers(self) -> Dict[str, Any]:

View File

@ -2,7 +2,7 @@
from __future__ import annotations
from typing import Any
from typing import Any, Tuple
from reflex import constants
from reflex.event import EventChain
@ -40,6 +40,15 @@ toggle_color_mode = BaseVar(
breakpoints = ["0", "30em", "48em", "62em", "80em", "96em"]
STYLE_PROP_SHORTHAND_MAPPING = {
"paddingX": ("padding-inline-start", "padding-inline-end"),
"paddingY": ("padding-top", "padding-bottom"),
"marginX": ("margin-inline-start", "margin-inline-end"),
"marginY": ("margin-top", "margin-bottom"),
"bg": ("background",),
"bgColor": ("background-color",),
}
def media_query(breakpoint_index: int):
"""Create a media query selector.
@ -110,21 +119,43 @@ def convert(style_dict):
"""
var_data = None # Track import/hook data from any Vars in the style dict.
out = {}
def update_out_dict(return_value, keys_to_update):
for k in keys_to_update:
out[k] = return_value
for key, value in style_dict.items():
key = format.to_camel_case(key, allow_hyphens=True)
keys = format_style_key(key)
if isinstance(value, dict):
# Recursively format nested style dictionaries.
out[key], new_var_data = convert(value)
return_val, new_var_data = convert(value)
update_out_dict(return_val, keys)
elif isinstance(value, list):
# Responsive value is a list of dict or value
out[key], new_var_data = convert_list(value)
return_val, new_var_data = convert_list(value)
update_out_dict(return_val, keys)
else:
out[key], new_var_data = convert_item(value)
return_val, new_var_data = convert_item(value)
update_out_dict(return_val, keys)
# Combine all the collected VarData instances.
var_data = VarData.merge(var_data, new_var_data)
return out, var_data
def format_style_key(key: str) -> Tuple[str, ...]:
"""Convert style keys to camel case and convert shorthand
styles names to their corresponding css names.
Args:
key: The style key to convert.
Returns:
Tuple of css style names corresponding to the key provided.
"""
key = format.to_camel_case(key, allow_hyphens=True)
return STYLE_PROP_SHORTHAND_MAPPING.get(key, (key,))
class Style(dict):
"""A style dictionary."""

View File

@ -3,6 +3,7 @@
import pytest
import reflex as rx
from reflex.components.core.debounce import DEFAULT_DEBOUNCE_TIMEOUT
from reflex.state import BaseState
from reflex.vars import BaseVar
@ -107,7 +108,7 @@ def test_full_control_implicit_debounce():
value=S.value,
on_change=S.on_change,
)._render()
assert tag.props["debounceTimeout"]._var_name == "50"
assert tag.props["debounceTimeout"]._var_name == str(DEFAULT_DEBOUNCE_TIMEOUT)
assert len(tag.props["onChange"].events) == 1
assert tag.props["onChange"].events[0].handler == S.on_change
assert tag.contents == ""
@ -119,7 +120,7 @@ def test_full_control_implicit_debounce_text_area():
value=S.value,
on_change=S.on_change,
)._render()
assert tag.props["debounceTimeout"]._var_name == "50"
assert tag.props["debounceTimeout"]._var_name == str(DEFAULT_DEBOUNCE_TIMEOUT)
assert len(tag.props["onChange"].events) == 1
assert tag.props["onChange"].events[0].handler == S.on_change
assert tag.contents == ""

View File

@ -56,4 +56,8 @@ def test_pass_custom_styles():
md = Markdown.create("# Hello", custom_styles={"h1": {"color": "red"}})
comp = md.get_component("h1") # type: ignore
assert comp.style == {"color": "red", "marginY": "0.5em"}
assert comp.style == {
"color": "red",
"margin-bottom": "0.5em",
"margin-top": "0.5em",
}

View File

@ -1274,7 +1274,6 @@ def test_app_wrap_priority(compilable_app):
"return ("
"<Box>"
"<ChakraProvider theme={extendTheme(theme)}>"
"<Global styles={GlobalStyles}/>"
"<ChakraColorModeProvider>"
"<Text>"
"<Fragment2>"

View File

@ -20,6 +20,19 @@ test_style = [
{"::-webkit-scrollbar": {"display": "none"}},
{"::-webkit-scrollbar": {"display": "none"}},
),
({"margin_y": "2rem"}, {"margin-bottom": "2rem", "margin-top": "2rem"}),
({"marginY": "2rem"}, {"margin-bottom": "2rem", "margin-top": "2rem"}),
(
{"::-webkit-scrollbar": {"bgColor": "red"}},
{"::-webkit-scrollbar": {"background-color": "red"}},
),
(
{"paddingX": ["2rem", "3rem"]},
{
"padding-inline-start": ["2rem", "3rem"],
"padding-inline-end": ["2rem", "3rem"],
},
),
]