Browse Source

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

Masen Furer 1 year ago
parent
commit
c124650082

+ 6 - 1
reflex/compiler/compiler.py

@@ -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"])
 
 

+ 0 - 49
reflex/components/chakra/base.py

@@ -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]:

+ 0 - 83
reflex/components/chakra/base.pyi

@@ -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

+ 2 - 8
reflex/components/chakra/forms/input.py

@@ -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)
 
 

+ 2 - 8
reflex/components/chakra/forms/textarea.py

@@ -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)

+ 3 - 1
reflex/components/core/debounce.py

@@ -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]

+ 2 - 0
reflex/components/core/debounce.pyi

@@ -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

+ 11 - 1
reflex/components/radix/primitives/accordion.py

@@ -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(
             {

+ 1 - 1
reflex/components/radix/primitives/accordion.pyi

@@ -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

+ 14 - 2
reflex/components/radix/themes/components/contextmenu.py

@@ -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."""

+ 2 - 2
reflex/components/radix/themes/components/contextmenu.pyi

@@ -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.

+ 13 - 1
reflex/components/radix/themes/components/dropdownmenu.py

@@ -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.
 

+ 1 - 1
reflex/components/radix/themes/components/dropdownmenu.pyi

@@ -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

+ 8 - 0
reflex/components/radix/themes/components/select.py

@@ -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."""

+ 38 - 3
reflex/components/radix/themes/components/slider.py

@@ -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

+ 23 - 22
reflex/components/radix/themes/components/slider.pyi

@@ -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.
         """
         ...
 

+ 42 - 1
reflex/components/radix/themes/components/table.py

@@ -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."""

+ 1 - 1
reflex/components/radix/themes/components/table.pyi

@@ -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

+ 3 - 1
reflex/components/radix/themes/components/tabs.py

@@ -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."""

+ 1 - 1
reflex/components/radix/themes/components/tabs.pyi

@@ -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

+ 2 - 8
reflex/components/radix/themes/components/textarea.py

@@ -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]:

+ 2 - 8
reflex/components/radix/themes/components/textfield.py

@@ -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]:

+ 36 - 5
reflex/style.py

@@ -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."""
 

+ 3 - 2
tests/components/forms/test_debounce.py

@@ -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 == ""

+ 5 - 1
tests/components/typography/test_markdown.py

@@ -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",
+    }

+ 0 - 1
tests/test_app.py

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

+ 13 - 0
tests/test_style.py

@@ -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"],
+        },
+    ),
 ]