Ver código fonte

[REF-1368] Move common form functionality to rx.el.forms (#2801)

* [REF-1368] Move common form functionality to rx.el.forms

Allow plain HTML Form element to have magic on_submit event handler.

* Chakra and Radix forms inherit `on_submit` functionality from rx.el.form

Consolidate logic in the basic HTML form and use it in both Radix and Chakra
form wrappers.

* from __future__ import annotations for py38
Masen Furer 1 ano atrás
pai
commit
5d647a498f

+ 34 - 7
integration/test_form_submit.py

@@ -1,4 +1,5 @@
 """Integration tests for forms."""
 """Integration tests for forms."""
+import functools
 import time
 import time
 from typing import Generator
 from typing import Generator
 
 
@@ -10,8 +11,12 @@ from reflex.testing import AppHarness
 from reflex.utils import format
 from reflex.utils import format
 
 
 
 
-def FormSubmit():
-    """App with a form using on_submit."""
+def FormSubmit(form_component):
+    """App with a form using on_submit.
+
+    Args:
+        form_component: The str name of the form component to use.
+    """
     import reflex as rx
     import reflex as rx
 
 
     class FormState(rx.State):
     class FormState(rx.State):
@@ -32,7 +37,7 @@ def FormSubmit():
                 is_read_only=True,
                 is_read_only=True,
                 id="token",
                 id="token",
             ),
             ),
-            rx.form.root(
+            eval(form_component)(
                 rx.vstack(
                 rx.vstack(
                     rx.chakra.input(id="name_input"),
                     rx.chakra.input(id="name_input"),
                     rx.hstack(rx.chakra.pin_input(length=4, id="pin_input")),
                     rx.hstack(rx.chakra.pin_input(length=4, id="pin_input")),
@@ -63,8 +68,12 @@ def FormSubmit():
         )
         )
 
 
 
 
-def FormSubmitName():
-    """App with a form using on_submit."""
+def FormSubmitName(form_component):
+    """App with a form using on_submit.
+
+    Args:
+        form_component: The str name of the form component to use.
+    """
     import reflex as rx
     import reflex as rx
 
 
     class FormState(rx.State):
     class FormState(rx.State):
@@ -85,7 +94,7 @@ def FormSubmitName():
                 is_read_only=True,
                 is_read_only=True,
                 id="token",
                 id="token",
             ),
             ),
-            rx.form.root(
+            eval(form_component)(
                 rx.vstack(
                 rx.vstack(
                     rx.chakra.input(name="name_input"),
                     rx.chakra.input(name="name_input"),
                     rx.hstack(rx.chakra.pin_input(length=4, name="pin_input")),
                     rx.hstack(rx.chakra.pin_input(length=4, name="pin_input")),
@@ -128,7 +137,23 @@ def FormSubmitName():
 
 
 
 
 @pytest.fixture(
 @pytest.fixture(
-    scope="session", params=[FormSubmit, FormSubmitName], ids=["id", "name"]
+    scope="session",
+    params=[
+        functools.partial(FormSubmit, form_component="rx.form.root"),
+        functools.partial(FormSubmitName, form_component="rx.form.root"),
+        functools.partial(FormSubmit, form_component="rx.el.form"),
+        functools.partial(FormSubmitName, form_component="rx.el.form"),
+        functools.partial(FormSubmit, form_component="rx.chakra.form"),
+        functools.partial(FormSubmitName, form_component="rx.chakra.form"),
+    ],
+    ids=[
+        "id-radix",
+        "name-radix",
+        "id-html",
+        "name-html",
+        "id-chakra",
+        "name-chakra",
+    ],
 )
 )
 def form_submit(request, tmp_path_factory) -> Generator[AppHarness, None, None]:
 def form_submit(request, tmp_path_factory) -> Generator[AppHarness, None, None]:
     """Start FormSubmit app at tmp_path via AppHarness.
     """Start FormSubmit app at tmp_path via AppHarness.
@@ -140,9 +165,11 @@ def form_submit(request, tmp_path_factory) -> Generator[AppHarness, None, None]:
     Yields:
     Yields:
         running AppHarness instance
         running AppHarness instance
     """
     """
+    param_id = request._pyfuncitem.callspec.id.replace("-", "_")
     with AppHarness.create(
     with AppHarness.create(
         root=tmp_path_factory.mktemp("form_submit"),
         root=tmp_path_factory.mktemp("form_submit"),
         app_source=request.param,  # type: ignore
         app_source=request.param,  # type: ignore
+        app_name=request.param.func.__name__ + f"_{param_id}",
     ) as harness:
     ) as harness:
         assert harness.app_instance is not None, "app is not running"
         assert harness.app_instance is not None, "app is not running"
         yield harness
         yield harness

+ 5 - 137
reflex/components/chakra/forms/form.py

@@ -1,39 +1,13 @@
 """Form components."""
 """Form components."""
 from __future__ import annotations
 from __future__ import annotations
 
 
-from hashlib import md5
-from typing import Any, Dict, Iterator
-
-from jinja2 import Environment
-
 from reflex.components.chakra import ChakraComponent
 from reflex.components.chakra import ChakraComponent
 from reflex.components.component import Component
 from reflex.components.component import Component
-from reflex.components.tags import Tag
-from reflex.constants import Dirs, EventTriggers
-from reflex.event import EventChain
-from reflex.utils import imports
-from reflex.utils.format import format_event_chain, to_camel_case
-from reflex.vars import BaseVar, Var
-
-FORM_DATA = Var.create("form_data")
-HANDLE_SUBMIT_JS_JINJA2 = Environment().from_string(
-    """
-    const handleSubmit_{{ handle_submit_unique_name }} = useCallback((ev) => {
-        const $form = ev.target
-        ev.preventDefault()
-        const {{ form_data }} = {...Object.fromEntries(new FormData($form).entries()), ...{{ field_ref_mapping }}}
-
-        {{ on_submit_event_chain }}
-
-        if ({{ reset_on_submit }}) {
-            $form.reset()
-        }
-    })
-    """
-)
-
-
-class Form(ChakraComponent):
+from reflex.components.el.elements.forms import Form as HTMLForm
+from reflex.vars import Var
+
+
+class Form(ChakraComponent, HTMLForm):
     """A form component."""
     """A form component."""
 
 
     tag = "Box"
     tag = "Box"
@@ -41,112 +15,6 @@ class Form(ChakraComponent):
     # What the form renders to.
     # What the form renders to.
     as_: Var[str] = "form"  # type: ignore
     as_: Var[str] = "form"  # type: ignore
 
 
-    # If true, the form will be cleared after submit.
-    reset_on_submit: Var[bool] = False  # type: ignore
-
-    # The name used to make this form's submit handler function unique
-    handle_submit_unique_name: Var[str]
-
-    @classmethod
-    def create(cls, *children, **props) -> Component:
-        """Create a form component.
-
-        Args:
-            *children: The children of the form.
-            **props: The properties of the form.
-
-        Returns:
-            The form component.
-        """
-        if "handle_submit_unique_name" in props:
-            return super().create(*children, **props)
-
-        # Render the form hooks and use the hash of the resulting code to create a unique name.
-        props["handle_submit_unique_name"] = ""
-        form = super().create(*children, **props)
-        code_hash = md5(str(form.get_hooks()).encode("utf-8")).hexdigest()
-        form.handle_submit_unique_name = code_hash
-        return form
-
-    def _get_imports(self) -> imports.ImportDict:
-        return imports.merge_imports(
-            super()._get_imports(),
-            {
-                "react": {imports.ImportVar(tag="useCallback")},
-                f"/{Dirs.STATE_PATH}": {
-                    imports.ImportVar(tag="getRefValue"),
-                    imports.ImportVar(tag="getRefValues"),
-                },
-            },
-        )
-
-    def _get_hooks(self) -> str | None:
-        if EventTriggers.ON_SUBMIT not in self.event_triggers:
-            return
-        return HANDLE_SUBMIT_JS_JINJA2.render(
-            handle_submit_unique_name=self.handle_submit_unique_name,
-            form_data=FORM_DATA,
-            field_ref_mapping=str(Var.create_safe(self._get_form_refs())),
-            on_submit_event_chain=format_event_chain(
-                self.event_triggers[EventTriggers.ON_SUBMIT]
-            ),
-            reset_on_submit=self.reset_on_submit,
-        )
-
-    def _render(self) -> Tag:
-        render_tag = (
-            super()
-            ._render()
-            .remove_props(
-                "reset_on_submit",
-                "handle_submit_unique_name",
-                to_camel_case(EventTriggers.ON_SUBMIT),
-            )
-        )
-        if EventTriggers.ON_SUBMIT in self.event_triggers:
-            render_tag.add_props(
-                **{
-                    EventTriggers.ON_SUBMIT: BaseVar(
-                        _var_name=f"handleSubmit_{self.handle_submit_unique_name}",
-                        _var_type=EventChain,
-                    )
-                }
-            )
-        return render_tag
-
-    def _get_form_refs(self) -> Dict[str, Any]:
-        # Send all the input refs to the handler.
-        form_refs = {}
-        for ref in self.get_refs():
-            # when ref start with refs_ it's an array of refs, so we need different method
-            # to collect data
-            if ref.startswith("refs_"):
-                ref_var = Var.create_safe(ref[:-3]).as_ref()
-                form_refs[ref[5:-3]] = Var.create_safe(
-                    f"getRefValues({str(ref_var)})", _var_is_local=False
-                )._replace(merge_var_data=ref_var._var_data)
-            else:
-                ref_var = Var.create_safe(ref).as_ref()
-                form_refs[ref[4:]] = Var.create_safe(
-                    f"getRefValue({str(ref_var)})", _var_is_local=False
-                )._replace(merge_var_data=ref_var._var_data)
-        return form_refs
-
-    def get_event_triggers(self) -> Dict[str, Any]:
-        """Get the event triggers that pass the component's value to the handler.
-
-        Returns:
-            A dict mapping the event trigger to the var that is passed to the handler.
-        """
-        return {
-            **super().get_event_triggers(),
-            EventTriggers.ON_SUBMIT: lambda e0: [FORM_DATA],
-        }
-
-    def _get_vars(self) -> Iterator[Var]:
-        yield from super()._get_vars()
-        yield from self._get_form_refs().values()
-
 
 
 class FormControl(ChakraComponent):
 class FormControl(ChakraComponent):
     """Provide context to form components."""
     """Provide context to form components."""

+ 94 - 17
reflex/components/chakra/forms/form.pyi

@@ -7,32 +7,85 @@ from typing import Any, Dict, Literal, Optional, Union, overload
 from reflex.vars import Var, BaseVar, ComputedVar
 from reflex.vars import Var, BaseVar, ComputedVar
 from reflex.event import EventChain, EventHandler, EventSpec
 from reflex.event import EventChain, EventHandler, EventSpec
 from reflex.style import Style
 from reflex.style import Style
-from hashlib import md5
-from typing import Any, Dict, Iterator
-from jinja2 import Environment
 from reflex.components.chakra import ChakraComponent
 from reflex.components.chakra import ChakraComponent
 from reflex.components.component import Component
 from reflex.components.component import Component
-from reflex.components.tags import Tag
-from reflex.constants import Dirs, EventTriggers
-from reflex.event import EventChain
-from reflex.utils import imports
-from reflex.utils.format import format_event_chain, to_camel_case
-from reflex.vars import BaseVar, Var
+from reflex.components.el.elements.forms import Form as HTMLForm
+from reflex.vars import Var
 
 
-FORM_DATA = Var.create("form_data")
-HANDLE_SUBMIT_JS_JINJA2 = Environment().from_string(
-    "\n    const handleSubmit_{{ handle_submit_unique_name }} = useCallback((ev) => {\n        const $form = ev.target\n        ev.preventDefault()\n        const {{ form_data }} = {...Object.fromEntries(new FormData($form).entries()), ...{{ field_ref_mapping }}}\n\n        {{ on_submit_event_chain }}\n\n        if ({{ reset_on_submit }}) {\n            $form.reset()\n        }\n    })\n    "
-)
-
-class Form(ChakraComponent):
+class Form(ChakraComponent, HTMLForm):
     @overload
     @overload
     @classmethod
     @classmethod
     def create(  # type: ignore
     def create(  # type: ignore
         cls,
         cls,
         *children,
         *children,
         as_: Optional[Union[Var[str], str]] = None,
         as_: Optional[Union[Var[str], str]] = None,
+        accept: Optional[
+            Union[Var[Union[str, int, bool]], Union[str, int, bool]]
+        ] = None,
+        accept_charset: Optional[
+            Union[Var[Union[str, int, bool]], Union[str, int, bool]]
+        ] = None,
+        action: Optional[
+            Union[Var[Union[str, int, bool]], Union[str, int, bool]]
+        ] = None,
+        auto_complete: Optional[
+            Union[Var[Union[str, int, bool]], Union[str, int, bool]]
+        ] = None,
+        enc_type: Optional[
+            Union[Var[Union[str, int, bool]], Union[str, int, bool]]
+        ] = None,
+        method: Optional[
+            Union[Var[Union[str, int, bool]], Union[str, int, bool]]
+        ] = None,
+        name: Optional[Union[Var[Union[str, int, bool]], Union[str, int, bool]]] = None,
+        no_validate: Optional[
+            Union[Var[Union[str, int, bool]], Union[str, int, bool]]
+        ] = None,
+        target: Optional[
+            Union[Var[Union[str, int, bool]], Union[str, int, bool]]
+        ] = None,
         reset_on_submit: Optional[Union[Var[bool], bool]] = None,
         reset_on_submit: Optional[Union[Var[bool], bool]] = None,
         handle_submit_unique_name: Optional[Union[Var[str], str]] = None,
         handle_submit_unique_name: Optional[Union[Var[str], str]] = None,
+        access_key: Optional[
+            Union[Var[Union[str, int, bool]], Union[str, int, bool]]
+        ] = None,
+        auto_capitalize: Optional[
+            Union[Var[Union[str, int, bool]], Union[str, int, bool]]
+        ] = None,
+        content_editable: Optional[
+            Union[Var[Union[str, int, bool]], Union[str, int, bool]]
+        ] = None,
+        context_menu: Optional[
+            Union[Var[Union[str, int, bool]], Union[str, int, bool]]
+        ] = None,
+        dir: Optional[Union[Var[Union[str, int, bool]], Union[str, int, bool]]] = None,
+        draggable: Optional[
+            Union[Var[Union[str, int, bool]], Union[str, int, bool]]
+        ] = None,
+        enter_key_hint: Optional[
+            Union[Var[Union[str, int, bool]], Union[str, int, bool]]
+        ] = None,
+        hidden: Optional[
+            Union[Var[Union[str, int, bool]], Union[str, int, bool]]
+        ] = None,
+        input_mode: Optional[
+            Union[Var[Union[str, int, bool]], Union[str, int, bool]]
+        ] = None,
+        item_prop: Optional[
+            Union[Var[Union[str, int, bool]], Union[str, int, bool]]
+        ] = None,
+        lang: Optional[Union[Var[Union[str, int, bool]], Union[str, int, bool]]] = None,
+        role: Optional[Union[Var[Union[str, int, bool]], Union[str, int, bool]]] = None,
+        slot: Optional[Union[Var[Union[str, int, bool]], Union[str, int, bool]]] = None,
+        spell_check: Optional[
+            Union[Var[Union[str, int, bool]], Union[str, int, bool]]
+        ] = None,
+        tab_index: Optional[
+            Union[Var[Union[str, int, bool]], Union[str, int, bool]]
+        ] = None,
+        title: Optional[
+            Union[Var[Union[str, int, bool]], Union[str, int, bool]]
+        ] = None,
         style: Optional[Style] = None,
         style: Optional[Style] = None,
         key: Optional[Any] = None,
         key: Optional[Any] = None,
         id: Optional[Any] = None,
         id: Optional[Any] = None,
@@ -94,8 +147,33 @@ class Form(ChakraComponent):
         Args:
         Args:
             *children: The children of the form.
             *children: The children of the form.
             as_: What the form renders to.
             as_: What the form renders to.
+            accept: MIME types the server accepts for file upload
+            accept_charset: Character encodings to be used for form submission
+            action: URL where the form's data should be submitted
+            auto_complete: Whether the form should have autocomplete enabled
+            enc_type: Encoding type for the form data when submitted
+            method: HTTP method to use for form submission
+            name: Name of the form
+            no_validate: Indicates that the form should not be validated on submit
+            target: Where to display the response after submitting the form
             reset_on_submit: If true, the form will be cleared after submit.
             reset_on_submit: If true, the form will be cleared after submit.
-            handle_submit_unique_name: The name used to make this form's submit handler function unique
+            handle_submit_unique_name: The name used to make this form's submit handler function unique.
+            access_key:  Provides a hint for generating a keyboard shortcut for the current element.
+            auto_capitalize: Controls whether and how text input is automatically capitalized as it is entered/edited by the user.
+            content_editable: Indicates whether the element's content is editable.
+            context_menu: Defines the ID of a <menu> element which will serve as the element's context menu.
+            dir: Defines the text direction. Allowed values are ltr (Left-To-Right) or rtl (Right-To-Left)
+            draggable: Defines whether the element can be dragged.
+            enter_key_hint: Hints what media types the media element is able to play.
+            hidden: Defines whether the element is hidden.
+            input_mode: Defines the type of the element.
+            item_prop: Defines the name of the element for metadata purposes.
+            lang: Defines the language used in the element.
+            role: Defines the role of the element.
+            slot: Assigns a slot in a shadow DOM shadow tree to an element.
+            spell_check: Defines whether the element may be checked for spelling errors.
+            tab_index: Defines the position of the current element in the tabbing order.
+            title: Defines a tooltip for the element.
             style: The style of the component.
             style: The style of the component.
             key: A unique key for the component.
             key: A unique key for the component.
             id: The id for the component.
             id: The id for the component.
@@ -108,7 +186,6 @@ class Form(ChakraComponent):
             The form component.
             The form component.
         """
         """
         ...
         ...
-    def get_event_triggers(self) -> Dict[str, Any]: ...
 
 
 class FormControl(ChakraComponent):
 class FormControl(ChakraComponent):
     @overload
     @overload

+ 134 - 3
reflex/components/el/elements/forms.py

@@ -1,12 +1,38 @@
 """Element classes. This is an auto-generated file. Do not edit. See ../generate.py."""
 """Element classes. This is an auto-generated file. Do not edit. See ../generate.py."""
-from typing import Any, Dict, Union
+from __future__ import annotations
+
+from hashlib import md5
+from typing import Any, Dict, Iterator, Union
+
+from jinja2 import Environment
 
 
 from reflex.components.el.element import Element
 from reflex.components.el.element import Element
-from reflex.constants.event import EventTriggers
-from reflex.vars import Var
+from reflex.components.tags.tag import Tag
+from reflex.constants import Dirs, EventTriggers
+from reflex.event import EventChain
+from reflex.utils import imports
+from reflex.utils.format import format_event_chain
+from reflex.vars import BaseVar, Var
 
 
 from .base import BaseHTML
 from .base import BaseHTML
 
 
+FORM_DATA = Var.create("form_data")
+HANDLE_SUBMIT_JS_JINJA2 = Environment().from_string(
+    """
+    const handleSubmit_{{ handle_submit_unique_name }} = useCallback((ev) => {
+        const $form = ev.target
+        ev.preventDefault()
+        const {{ form_data }} = {...Object.fromEntries(new FormData($form).entries()), ...{{ field_ref_mapping }}}
+
+        {{ on_submit_event_chain }}
+
+        if ({{ reset_on_submit }}) {
+            $form.reset()
+        }
+    })
+    """
+)
+
 
 
 class Button(BaseHTML):
 class Button(BaseHTML):
     """Display the button element."""
     """Display the button element."""
@@ -101,6 +127,111 @@ class Form(BaseHTML):
     # Where to display the response after submitting the form
     # Where to display the response after submitting the form
     target: Var[Union[str, int, bool]]
     target: Var[Union[str, int, bool]]
 
 
+    # If true, the form will be cleared after submit.
+    reset_on_submit: Var[bool] = False  # type: ignore
+
+    # The name used to make this form's submit handler function unique.
+    handle_submit_unique_name: Var[str]
+
+    def get_event_triggers(self) -> Dict[str, Any]:
+        """Event triggers for radix form root.
+
+        Returns:
+            The triggers for event supported by Root.
+        """
+        return {
+            **super().get_event_triggers(),
+            EventTriggers.ON_SUBMIT: lambda e0: [FORM_DATA],
+        }
+
+    @classmethod
+    def create(cls, *children, **props):
+        """Create a form component.
+
+        Args:
+            *children: The children of the form.
+            **props: The properties of the form.
+
+        Returns:
+            The form component.
+        """
+        if "handle_submit_unique_name" in props:
+            return super().create(*children, **props)
+
+        # Render the form hooks and use the hash of the resulting code to create a unique name.
+        props["handle_submit_unique_name"] = ""
+        form = super().create(*children, **props)
+        form.handle_submit_unique_name = md5(
+            str(form.get_hooks()).encode("utf-8")
+        ).hexdigest()
+        return form
+
+    def _get_imports(self) -> imports.ImportDict:
+        return imports.merge_imports(
+            super()._get_imports(),
+            {
+                "react": {imports.ImportVar(tag="useCallback")},
+                f"/{Dirs.STATE_PATH}": {
+                    imports.ImportVar(tag="getRefValue"),
+                    imports.ImportVar(tag="getRefValues"),
+                },
+            },
+        )
+
+    def _get_hooks(self) -> str | None:
+        if EventTriggers.ON_SUBMIT not in self.event_triggers:
+            return
+        return HANDLE_SUBMIT_JS_JINJA2.render(
+            handle_submit_unique_name=self.handle_submit_unique_name,
+            form_data=FORM_DATA,
+            field_ref_mapping=str(Var.create_safe(self._get_form_refs())),
+            on_submit_event_chain=format_event_chain(
+                self.event_triggers[EventTriggers.ON_SUBMIT]
+            ),
+            reset_on_submit=self.reset_on_submit,
+        )
+
+    def _render(self) -> Tag:
+        render_tag = super()._render()
+        if EventTriggers.ON_SUBMIT in self.event_triggers:
+            render_tag.add_props(
+                **{
+                    EventTriggers.ON_SUBMIT: BaseVar(
+                        _var_name=f"handleSubmit_{self.handle_submit_unique_name}",
+                        _var_type=EventChain,
+                    )
+                }
+            )
+        return render_tag
+
+    def _get_form_refs(self) -> Dict[str, Any]:
+        # Send all the input refs to the handler.
+        form_refs = {}
+        for ref in self.get_refs():
+            # when ref start with refs_ it's an array of refs, so we need different method
+            # to collect data
+            if ref.startswith("refs_"):
+                ref_var = Var.create_safe(ref[:-3]).as_ref()
+                form_refs[ref[5:-3]] = Var.create_safe(
+                    f"getRefValues({str(ref_var)})", _var_is_local=False
+                )._replace(merge_var_data=ref_var._var_data)
+            else:
+                ref_var = Var.create_safe(ref).as_ref()
+                form_refs[ref[4:]] = Var.create_safe(
+                    f"getRefValue({str(ref_var)})", _var_is_local=False
+                )._replace(merge_var_data=ref_var._var_data)
+        return form_refs
+
+    def _get_vars(self) -> Iterator[Var]:
+        yield from super()._get_vars()
+        yield from self._get_form_refs().values()
+
+    def _exclude_props(self) -> list[str]:
+        return super()._exclude_props() + [
+            "reset_on_submit",
+            "handle_submit_unique_name",
+        ]
+
 
 
 class Input(BaseHTML):
 class Input(BaseHTML):
     """Display the input element."""
     """Display the input element."""

+ 26 - 10
reflex/components/el/elements/forms.pyi

@@ -7,12 +7,23 @@ from typing import Any, Dict, Literal, Optional, Union, overload
 from reflex.vars import Var, BaseVar, ComputedVar
 from reflex.vars import Var, BaseVar, ComputedVar
 from reflex.event import EventChain, EventHandler, EventSpec
 from reflex.event import EventChain, EventHandler, EventSpec
 from reflex.style import Style
 from reflex.style import Style
-from typing import Any, Dict, Union
+from hashlib import md5
+from typing import Any, Dict, Iterator, Union
+from jinja2 import Environment
 from reflex.components.el.element import Element
 from reflex.components.el.element import Element
-from reflex.constants.event import EventTriggers
-from reflex.vars import Var
+from reflex.components.tags.tag import Tag
+from reflex.constants import Dirs, EventTriggers
+from reflex.event import EventChain
+from reflex.utils import imports
+from reflex.utils.format import format_event_chain
+from reflex.vars import BaseVar, Var
 from .base import BaseHTML
 from .base import BaseHTML
 
 
+FORM_DATA = Var.create("form_data")
+HANDLE_SUBMIT_JS_JINJA2 = Environment().from_string(
+    "\n    const handleSubmit_{{ handle_submit_unique_name }} = useCallback((ev) => {\n        const $form = ev.target\n        ev.preventDefault()\n        const {{ form_data }} = {...Object.fromEntries(new FormData($form).entries()), ...{{ field_ref_mapping }}}\n\n        {{ on_submit_event_chain }}\n\n        if ({{ reset_on_submit }}) {\n            $form.reset()\n        }\n    })\n    "
+)
+
 class Button(BaseHTML):
 class Button(BaseHTML):
     @overload
     @overload
     @classmethod
     @classmethod
@@ -407,6 +418,7 @@ class Fieldset(Element):
         ...
         ...
 
 
 class Form(BaseHTML):
 class Form(BaseHTML):
+    def get_event_triggers(self) -> Dict[str, Any]: ...
     @overload
     @overload
     @classmethod
     @classmethod
     def create(  # type: ignore
     def create(  # type: ignore
@@ -437,6 +449,8 @@ class Form(BaseHTML):
         target: Optional[
         target: Optional[
             Union[Var[Union[str, int, bool]], Union[str, int, bool]]
             Union[Var[Union[str, int, bool]], Union[str, int, bool]]
         ] = None,
         ] = None,
+        reset_on_submit: Optional[Union[Var[bool], bool]] = None,
+        handle_submit_unique_name: Optional[Union[Var[str], str]] = None,
         access_key: Optional[
         access_key: Optional[
             Union[Var[Union[str, int, bool]], Union[str, int, bool]]
             Union[Var[Union[str, int, bool]], Union[str, int, bool]]
         ] = None,
         ] = None,
@@ -525,15 +539,18 @@ class Form(BaseHTML):
         on_scroll: Optional[
         on_scroll: Optional[
             Union[EventHandler, EventSpec, list, function, BaseVar]
             Union[EventHandler, EventSpec, list, function, BaseVar]
         ] = None,
         ] = None,
+        on_submit: Optional[
+            Union[EventHandler, EventSpec, list, function, BaseVar]
+        ] = None,
         on_unmount: Optional[
         on_unmount: Optional[
             Union[EventHandler, EventSpec, list, function, BaseVar]
             Union[EventHandler, EventSpec, list, function, BaseVar]
         ] = None,
         ] = None,
         **props
         **props
     ) -> "Form":
     ) -> "Form":
-        """Create the component.
+        """Create a form component.
 
 
         Args:
         Args:
-            *children: The children of the component.
+            *children: The children of the form.
             accept: MIME types the server accepts for file upload
             accept: MIME types the server accepts for file upload
             accept_charset: Character encodings to be used for form submission
             accept_charset: Character encodings to be used for form submission
             action: URL where the form's data should be submitted
             action: URL where the form's data should be submitted
@@ -543,6 +560,8 @@ class Form(BaseHTML):
             name: Name of the form
             name: Name of the form
             no_validate: Indicates that the form should not be validated on submit
             no_validate: Indicates that the form should not be validated on submit
             target: Where to display the response after submitting the form
             target: Where to display the response after submitting the form
+            reset_on_submit: If true, the form will be cleared after submit.
+            handle_submit_unique_name: The name used to make this form's submit handler function unique.
             access_key:  Provides a hint for generating a keyboard shortcut for the current element.
             access_key:  Provides a hint for generating a keyboard shortcut for the current element.
             auto_capitalize: Controls whether and how text input is automatically capitalized as it is entered/edited by the user.
             auto_capitalize: Controls whether and how text input is automatically capitalized as it is entered/edited by the user.
             content_editable: Indicates whether the element's content is editable.
             content_editable: Indicates whether the element's content is editable.
@@ -565,13 +584,10 @@ class Form(BaseHTML):
             class_name: The class name for the component.
             class_name: The class name for the component.
             autofocus: Whether the component should take the focus once the page is loaded
             autofocus: Whether the component should take the focus once the page is loaded
             custom_attrs: custom attribute
             custom_attrs: custom attribute
-            **props: The props of the component.
+            **props: The properties of the form.
 
 
         Returns:
         Returns:
-            The component.
-
-        Raises:
-            TypeError: If an invalid child is passed.
+            The form component.
         """
         """
         ...
         ...
 
 

+ 4 - 125
reflex/components/radix/primitives/form.py

@@ -2,40 +2,16 @@
 
 
 from __future__ import annotations
 from __future__ import annotations
 
 
-from hashlib import md5
-from typing import Any, Dict, Iterator, Literal
-
-from jinja2 import Environment
+from typing import Any, Dict, Literal
 
 
 from reflex.components.component import Component, ComponentNamespace
 from reflex.components.component import Component, ComponentNamespace
+from reflex.components.el.elements.forms import Form as HTMLForm
 from reflex.components.radix.themes.components.text_field import TextFieldInput
 from reflex.components.radix.themes.components.text_field import TextFieldInput
-from reflex.components.tags.tag import Tag
-from reflex.constants.base import Dirs
 from reflex.constants.event import EventTriggers
 from reflex.constants.event import EventTriggers
-from reflex.event import EventChain
-from reflex.utils import imports
-from reflex.utils.format import format_event_chain, to_camel_case
-from reflex.vars import BaseVar, Var
+from reflex.vars import Var
 
 
 from .base import RadixPrimitiveComponentWithClassName
 from .base import RadixPrimitiveComponentWithClassName
 
 
-FORM_DATA = Var.create("form_data")
-HANDLE_SUBMIT_JS_JINJA2 = Environment().from_string(
-    """
-    const handleSubmit_{{ handle_submit_unique_name }} = useCallback((ev) => {
-        const $form = ev.target
-        ev.preventDefault()
-        const {{ form_data }} = {...Object.fromEntries(new FormData($form).entries()), ...{{ field_ref_mapping }}}
-
-        {{ on_submit_event_chain }}
-
-        if ({{ reset_on_submit }}) {
-            $form.reset()
-        }
-    })
-    """
-)
-
 
 
 class FormComponent(RadixPrimitiveComponentWithClassName):
 class FormComponent(RadixPrimitiveComponentWithClassName):
     """Base class for all @radix-ui/react-form components."""
     """Base class for all @radix-ui/react-form components."""
@@ -43,19 +19,13 @@ class FormComponent(RadixPrimitiveComponentWithClassName):
     library = "@radix-ui/react-form@^0.0.3"
     library = "@radix-ui/react-form@^0.0.3"
 
 
 
 
-class FormRoot(FormComponent):
+class FormRoot(FormComponent, HTMLForm):
     """The root component of a radix form."""
     """The root component of a radix form."""
 
 
     tag = "Root"
     tag = "Root"
 
 
     alias = "RadixFormRoot"
     alias = "RadixFormRoot"
 
 
-    # If true, the form will be cleared after submit.
-    reset_on_submit: Var[bool] = False  # type: ignore
-
-    # The name used to make this form's submit handler function unique.
-    handle_submit_unique_name: Var[str]
-
     def get_event_triggers(self) -> Dict[str, Any]:
     def get_event_triggers(self) -> Dict[str, Any]:
         """Event triggers for radix form root.
         """Event triggers for radix form root.
 
 
@@ -64,106 +34,15 @@ class FormRoot(FormComponent):
         """
         """
         return {
         return {
             **super().get_event_triggers(),
             **super().get_event_triggers(),
-            EventTriggers.ON_SUBMIT: lambda e0: [FORM_DATA],
             EventTriggers.ON_CLEAR_SERVER_ERRORS: lambda: [],
             EventTriggers.ON_CLEAR_SERVER_ERRORS: lambda: [],
         }
         }
 
 
-    @classmethod
-    def create(cls, *children, **props):
-        """Create a form component.
-
-        Args:
-            *children: The children of the form.
-            **props: The properties of the form.
-
-        Returns:
-            The form component.
-        """
-        if "handle_submit_unique_name" in props:
-            return super().create(*children, **props)
-
-        # Render the form hooks and use the hash of the resulting code to create a unique name.
-        props["handle_submit_unique_name"] = ""
-        form = super().create(*children, **props)
-        form.handle_submit_unique_name = md5(
-            str(form.get_hooks()).encode("utf-8")
-        ).hexdigest()
-        return form
-
-    def _get_imports(self) -> imports.ImportDict:
-        return imports.merge_imports(
-            super()._get_imports(),
-            {
-                "react": {imports.ImportVar(tag="useCallback")},
-                f"/{Dirs.STATE_PATH}": {
-                    imports.ImportVar(tag="getRefValue"),
-                    imports.ImportVar(tag="getRefValues"),
-                },
-            },
-        )
-
-    def _get_hooks(self) -> str | None:
-        if EventTriggers.ON_SUBMIT not in self.event_triggers:
-            return
-        return HANDLE_SUBMIT_JS_JINJA2.render(
-            handle_submit_unique_name=self.handle_submit_unique_name,
-            form_data=FORM_DATA,
-            field_ref_mapping=str(Var.create_safe(self._get_form_refs())),
-            on_submit_event_chain=format_event_chain(
-                self.event_triggers[EventTriggers.ON_SUBMIT]
-            ),
-            reset_on_submit=self.reset_on_submit,
-        )
-
-    def _render(self) -> Tag:
-        render_tag = (
-            super()
-            ._render()
-            .remove_props(
-                "reset_on_submit",
-                "handle_submit_unique_name",
-                to_camel_case(EventTriggers.ON_SUBMIT),
-            )
-        )
-        if EventTriggers.ON_SUBMIT in self.event_triggers:
-            render_tag.add_props(
-                **{
-                    EventTriggers.ON_SUBMIT: BaseVar(
-                        _var_name=f"handleSubmit_{self.handle_submit_unique_name}",
-                        _var_type=EventChain,
-                    )
-                }
-            )
-        return render_tag
-
-    def _get_form_refs(self) -> Dict[str, Any]:
-        # Send all the input refs to the handler.
-        form_refs = {}
-        for ref in self.get_refs():
-            # when ref start with refs_ it's an array of refs, so we need different method
-            # to collect data
-            if ref.startswith("refs_"):
-                ref_var = Var.create_safe(ref[:-3]).as_ref()
-                form_refs[ref[5:-3]] = Var.create_safe(
-                    f"getRefValues({str(ref_var)})", _var_is_local=False
-                )._replace(merge_var_data=ref_var._var_data)
-            else:
-                ref_var = Var.create_safe(ref).as_ref()
-                form_refs[ref[4:]] = Var.create_safe(
-                    f"getRefValue({str(ref_var)})", _var_is_local=False
-                )._replace(merge_var_data=ref_var._var_data)
-        return form_refs
-
     def _apply_theme(self, theme: Component):
     def _apply_theme(self, theme: Component):
         return {
         return {
             "width": "260px",
             "width": "260px",
             **self.style,
             **self.style,
         }
         }
 
 
-    def _get_vars(self) -> Iterator[Var]:
-        yield from super()._get_vars()
-        yield from self._get_form_refs().values()
-
 
 
 class FormField(FormComponent):
 class FormField(FormComponent):
     """A form field component."""
     """A form field component."""

+ 280 - 21
reflex/components/radix/primitives/form.pyi

@@ -7,25 +7,14 @@ from typing import Any, Dict, Literal, Optional, Union, overload
 from reflex.vars import Var, BaseVar, ComputedVar
 from reflex.vars import Var, BaseVar, ComputedVar
 from reflex.event import EventChain, EventHandler, EventSpec
 from reflex.event import EventChain, EventHandler, EventSpec
 from reflex.style import Style
 from reflex.style import Style
-from hashlib import md5
-from typing import Any, Dict, Iterator, Literal
-from jinja2 import Environment
+from typing import Any, Dict, Literal
 from reflex.components.component import Component, ComponentNamespace
 from reflex.components.component import Component, ComponentNamespace
+from reflex.components.el.elements.forms import Form as HTMLForm
 from reflex.components.radix.themes.components.text_field import TextFieldInput
 from reflex.components.radix.themes.components.text_field import TextFieldInput
-from reflex.components.tags.tag import Tag
-from reflex.constants.base import Dirs
 from reflex.constants.event import EventTriggers
 from reflex.constants.event import EventTriggers
-from reflex.event import EventChain
-from reflex.utils import imports
-from reflex.utils.format import format_event_chain, to_camel_case
-from reflex.vars import BaseVar, Var
+from reflex.vars import Var
 from .base import RadixPrimitiveComponentWithClassName
 from .base import RadixPrimitiveComponentWithClassName
 
 
-FORM_DATA = Var.create("form_data")
-HANDLE_SUBMIT_JS_JINJA2 = Environment().from_string(
-    "\n    const handleSubmit_{{ handle_submit_unique_name }} = useCallback((ev) => {\n        const $form = ev.target\n        ev.preventDefault()\n        const {{ form_data }} = {...Object.fromEntries(new FormData($form).entries()), ...{{ field_ref_mapping }}}\n\n        {{ on_submit_event_chain }}\n\n        if ({{ reset_on_submit }}) {\n            $form.reset()\n        }\n    })\n    "
-)
-
 class FormComponent(RadixPrimitiveComponentWithClassName):
 class FormComponent(RadixPrimitiveComponentWithClassName):
     @overload
     @overload
     @classmethod
     @classmethod
@@ -107,16 +96,81 @@ class FormComponent(RadixPrimitiveComponentWithClassName):
         """
         """
         ...
         ...
 
 
-class FormRoot(FormComponent):
+class FormRoot(FormComponent, HTMLForm):
     def get_event_triggers(self) -> Dict[str, Any]: ...
     def get_event_triggers(self) -> Dict[str, Any]: ...
     @overload
     @overload
     @classmethod
     @classmethod
     def create(  # type: ignore
     def create(  # type: ignore
         cls,
         cls,
         *children,
         *children,
+        as_child: Optional[Union[Var[bool], bool]] = None,
+        accept: Optional[
+            Union[Var[Union[str, int, bool]], Union[str, int, bool]]
+        ] = None,
+        accept_charset: Optional[
+            Union[Var[Union[str, int, bool]], Union[str, int, bool]]
+        ] = None,
+        action: Optional[
+            Union[Var[Union[str, int, bool]], Union[str, int, bool]]
+        ] = None,
+        auto_complete: Optional[
+            Union[Var[Union[str, int, bool]], Union[str, int, bool]]
+        ] = None,
+        enc_type: Optional[
+            Union[Var[Union[str, int, bool]], Union[str, int, bool]]
+        ] = None,
+        method: Optional[
+            Union[Var[Union[str, int, bool]], Union[str, int, bool]]
+        ] = None,
+        name: Optional[Union[Var[Union[str, int, bool]], Union[str, int, bool]]] = None,
+        no_validate: Optional[
+            Union[Var[Union[str, int, bool]], Union[str, int, bool]]
+        ] = None,
+        target: Optional[
+            Union[Var[Union[str, int, bool]], Union[str, int, bool]]
+        ] = None,
         reset_on_submit: Optional[Union[Var[bool], bool]] = None,
         reset_on_submit: Optional[Union[Var[bool], bool]] = None,
         handle_submit_unique_name: Optional[Union[Var[str], str]] = None,
         handle_submit_unique_name: Optional[Union[Var[str], str]] = None,
-        as_child: Optional[Union[Var[bool], bool]] = None,
+        access_key: Optional[
+            Union[Var[Union[str, int, bool]], Union[str, int, bool]]
+        ] = None,
+        auto_capitalize: Optional[
+            Union[Var[Union[str, int, bool]], Union[str, int, bool]]
+        ] = None,
+        content_editable: Optional[
+            Union[Var[Union[str, int, bool]], Union[str, int, bool]]
+        ] = None,
+        context_menu: Optional[
+            Union[Var[Union[str, int, bool]], Union[str, int, bool]]
+        ] = None,
+        dir: Optional[Union[Var[Union[str, int, bool]], Union[str, int, bool]]] = None,
+        draggable: Optional[
+            Union[Var[Union[str, int, bool]], Union[str, int, bool]]
+        ] = None,
+        enter_key_hint: Optional[
+            Union[Var[Union[str, int, bool]], Union[str, int, bool]]
+        ] = None,
+        hidden: Optional[
+            Union[Var[Union[str, int, bool]], Union[str, int, bool]]
+        ] = None,
+        input_mode: Optional[
+            Union[Var[Union[str, int, bool]], Union[str, int, bool]]
+        ] = None,
+        item_prop: Optional[
+            Union[Var[Union[str, int, bool]], Union[str, int, bool]]
+        ] = None,
+        lang: Optional[Union[Var[Union[str, int, bool]], Union[str, int, bool]]] = None,
+        role: Optional[Union[Var[Union[str, int, bool]], Union[str, int, bool]]] = None,
+        slot: Optional[Union[Var[Union[str, int, bool]], Union[str, int, bool]]] = None,
+        spell_check: Optional[
+            Union[Var[Union[str, int, bool]], Union[str, int, bool]]
+        ] = None,
+        tab_index: Optional[
+            Union[Var[Union[str, int, bool]], Union[str, int, bool]]
+        ] = None,
+        title: Optional[
+            Union[Var[Union[str, int, bool]], Union[str, int, bool]]
+        ] = None,
         style: Optional[Style] = None,
         style: Optional[Style] = None,
         key: Optional[Any] = None,
         key: Optional[Any] = None,
         id: Optional[Any] = None,
         id: Optional[Any] = None,
@@ -180,9 +234,34 @@ class FormRoot(FormComponent):
 
 
         Args:
         Args:
             *children: The children of the form.
             *children: The children of the form.
+            as_child: Change the default rendered element for the one passed as a child.
+            accept: MIME types the server accepts for file upload
+            accept_charset: Character encodings to be used for form submission
+            action: URL where the form's data should be submitted
+            auto_complete: Whether the form should have autocomplete enabled
+            enc_type: Encoding type for the form data when submitted
+            method: HTTP method to use for form submission
+            name: Name of the form
+            no_validate: Indicates that the form should not be validated on submit
+            target: Where to display the response after submitting the form
             reset_on_submit: If true, the form will be cleared after submit.
             reset_on_submit: If true, the form will be cleared after submit.
             handle_submit_unique_name: The name used to make this form's submit handler function unique.
             handle_submit_unique_name: The name used to make this form's submit handler function unique.
-            as_child: Change the default rendered element for the one passed as a child.
+            access_key:  Provides a hint for generating a keyboard shortcut for the current element.
+            auto_capitalize: Controls whether and how text input is automatically capitalized as it is entered/edited by the user.
+            content_editable: Indicates whether the element's content is editable.
+            context_menu: Defines the ID of a <menu> element which will serve as the element's context menu.
+            dir: Defines the text direction. Allowed values are ltr (Left-To-Right) or rtl (Right-To-Left)
+            draggable: Defines whether the element can be dragged.
+            enter_key_hint: Hints what media types the media element is able to play.
+            hidden: Defines whether the element is hidden.
+            input_mode: Defines the type of the element.
+            item_prop: Defines the name of the element for metadata purposes.
+            lang: Defines the language used in the element.
+            role: Defines the role of the element.
+            slot: Assigns a slot in a shadow DOM shadow tree to an element.
+            spell_check: Defines whether the element may be checked for spelling errors.
+            tab_index: Defines the position of the current element in the tabbing order.
+            title: Defines a tooltip for the element.
             style: The style of the component.
             style: The style of the component.
             key: A unique key for the component.
             key: A unique key for the component.
             id: The id for the component.
             id: The id for the component.
@@ -743,9 +822,74 @@ class Form(FormRoot):
     def create(  # type: ignore
     def create(  # type: ignore
         cls,
         cls,
         *children,
         *children,
+        as_child: Optional[Union[Var[bool], bool]] = None,
+        accept: Optional[
+            Union[Var[Union[str, int, bool]], Union[str, int, bool]]
+        ] = None,
+        accept_charset: Optional[
+            Union[Var[Union[str, int, bool]], Union[str, int, bool]]
+        ] = None,
+        action: Optional[
+            Union[Var[Union[str, int, bool]], Union[str, int, bool]]
+        ] = None,
+        auto_complete: Optional[
+            Union[Var[Union[str, int, bool]], Union[str, int, bool]]
+        ] = None,
+        enc_type: Optional[
+            Union[Var[Union[str, int, bool]], Union[str, int, bool]]
+        ] = None,
+        method: Optional[
+            Union[Var[Union[str, int, bool]], Union[str, int, bool]]
+        ] = None,
+        name: Optional[Union[Var[Union[str, int, bool]], Union[str, int, bool]]] = None,
+        no_validate: Optional[
+            Union[Var[Union[str, int, bool]], Union[str, int, bool]]
+        ] = None,
+        target: Optional[
+            Union[Var[Union[str, int, bool]], Union[str, int, bool]]
+        ] = None,
         reset_on_submit: Optional[Union[Var[bool], bool]] = None,
         reset_on_submit: Optional[Union[Var[bool], bool]] = None,
         handle_submit_unique_name: Optional[Union[Var[str], str]] = None,
         handle_submit_unique_name: Optional[Union[Var[str], str]] = None,
-        as_child: Optional[Union[Var[bool], bool]] = None,
+        access_key: Optional[
+            Union[Var[Union[str, int, bool]], Union[str, int, bool]]
+        ] = None,
+        auto_capitalize: Optional[
+            Union[Var[Union[str, int, bool]], Union[str, int, bool]]
+        ] = None,
+        content_editable: Optional[
+            Union[Var[Union[str, int, bool]], Union[str, int, bool]]
+        ] = None,
+        context_menu: Optional[
+            Union[Var[Union[str, int, bool]], Union[str, int, bool]]
+        ] = None,
+        dir: Optional[Union[Var[Union[str, int, bool]], Union[str, int, bool]]] = None,
+        draggable: Optional[
+            Union[Var[Union[str, int, bool]], Union[str, int, bool]]
+        ] = None,
+        enter_key_hint: Optional[
+            Union[Var[Union[str, int, bool]], Union[str, int, bool]]
+        ] = None,
+        hidden: Optional[
+            Union[Var[Union[str, int, bool]], Union[str, int, bool]]
+        ] = None,
+        input_mode: Optional[
+            Union[Var[Union[str, int, bool]], Union[str, int, bool]]
+        ] = None,
+        item_prop: Optional[
+            Union[Var[Union[str, int, bool]], Union[str, int, bool]]
+        ] = None,
+        lang: Optional[Union[Var[Union[str, int, bool]], Union[str, int, bool]]] = None,
+        role: Optional[Union[Var[Union[str, int, bool]], Union[str, int, bool]]] = None,
+        slot: Optional[Union[Var[Union[str, int, bool]], Union[str, int, bool]]] = None,
+        spell_check: Optional[
+            Union[Var[Union[str, int, bool]], Union[str, int, bool]]
+        ] = None,
+        tab_index: Optional[
+            Union[Var[Union[str, int, bool]], Union[str, int, bool]]
+        ] = None,
+        title: Optional[
+            Union[Var[Union[str, int, bool]], Union[str, int, bool]]
+        ] = None,
         style: Optional[Style] = None,
         style: Optional[Style] = None,
         key: Optional[Any] = None,
         key: Optional[Any] = None,
         id: Optional[Any] = None,
         id: Optional[Any] = None,
@@ -809,9 +953,34 @@ class Form(FormRoot):
 
 
         Args:
         Args:
             *children: The children of the form.
             *children: The children of the form.
+            as_child: Change the default rendered element for the one passed as a child.
+            accept: MIME types the server accepts for file upload
+            accept_charset: Character encodings to be used for form submission
+            action: URL where the form's data should be submitted
+            auto_complete: Whether the form should have autocomplete enabled
+            enc_type: Encoding type for the form data when submitted
+            method: HTTP method to use for form submission
+            name: Name of the form
+            no_validate: Indicates that the form should not be validated on submit
+            target: Where to display the response after submitting the form
             reset_on_submit: If true, the form will be cleared after submit.
             reset_on_submit: If true, the form will be cleared after submit.
             handle_submit_unique_name: The name used to make this form's submit handler function unique.
             handle_submit_unique_name: The name used to make this form's submit handler function unique.
-            as_child: Change the default rendered element for the one passed as a child.
+            access_key:  Provides a hint for generating a keyboard shortcut for the current element.
+            auto_capitalize: Controls whether and how text input is automatically capitalized as it is entered/edited by the user.
+            content_editable: Indicates whether the element's content is editable.
+            context_menu: Defines the ID of a <menu> element which will serve as the element's context menu.
+            dir: Defines the text direction. Allowed values are ltr (Left-To-Right) or rtl (Right-To-Left)
+            draggable: Defines whether the element can be dragged.
+            enter_key_hint: Hints what media types the media element is able to play.
+            hidden: Defines whether the element is hidden.
+            input_mode: Defines the type of the element.
+            item_prop: Defines the name of the element for metadata purposes.
+            lang: Defines the language used in the element.
+            role: Defines the role of the element.
+            slot: Assigns a slot in a shadow DOM shadow tree to an element.
+            spell_check: Defines whether the element may be checked for spelling errors.
+            tab_index: Defines the position of the current element in the tabbing order.
+            title: Defines a tooltip for the element.
             style: The style of the component.
             style: The style of the component.
             key: A unique key for the component.
             key: A unique key for the component.
             id: The id for the component.
             id: The id for the component.
@@ -837,9 +1006,74 @@ class FormNamespace(ComponentNamespace):
     @staticmethod
     @staticmethod
     def __call__(
     def __call__(
         *children,
         *children,
+        as_child: Optional[Union[Var[bool], bool]] = None,
+        accept: Optional[
+            Union[Var[Union[str, int, bool]], Union[str, int, bool]]
+        ] = None,
+        accept_charset: Optional[
+            Union[Var[Union[str, int, bool]], Union[str, int, bool]]
+        ] = None,
+        action: Optional[
+            Union[Var[Union[str, int, bool]], Union[str, int, bool]]
+        ] = None,
+        auto_complete: Optional[
+            Union[Var[Union[str, int, bool]], Union[str, int, bool]]
+        ] = None,
+        enc_type: Optional[
+            Union[Var[Union[str, int, bool]], Union[str, int, bool]]
+        ] = None,
+        method: Optional[
+            Union[Var[Union[str, int, bool]], Union[str, int, bool]]
+        ] = None,
+        name: Optional[Union[Var[Union[str, int, bool]], Union[str, int, bool]]] = None,
+        no_validate: Optional[
+            Union[Var[Union[str, int, bool]], Union[str, int, bool]]
+        ] = None,
+        target: Optional[
+            Union[Var[Union[str, int, bool]], Union[str, int, bool]]
+        ] = None,
         reset_on_submit: Optional[Union[Var[bool], bool]] = None,
         reset_on_submit: Optional[Union[Var[bool], bool]] = None,
         handle_submit_unique_name: Optional[Union[Var[str], str]] = None,
         handle_submit_unique_name: Optional[Union[Var[str], str]] = None,
-        as_child: Optional[Union[Var[bool], bool]] = None,
+        access_key: Optional[
+            Union[Var[Union[str, int, bool]], Union[str, int, bool]]
+        ] = None,
+        auto_capitalize: Optional[
+            Union[Var[Union[str, int, bool]], Union[str, int, bool]]
+        ] = None,
+        content_editable: Optional[
+            Union[Var[Union[str, int, bool]], Union[str, int, bool]]
+        ] = None,
+        context_menu: Optional[
+            Union[Var[Union[str, int, bool]], Union[str, int, bool]]
+        ] = None,
+        dir: Optional[Union[Var[Union[str, int, bool]], Union[str, int, bool]]] = None,
+        draggable: Optional[
+            Union[Var[Union[str, int, bool]], Union[str, int, bool]]
+        ] = None,
+        enter_key_hint: Optional[
+            Union[Var[Union[str, int, bool]], Union[str, int, bool]]
+        ] = None,
+        hidden: Optional[
+            Union[Var[Union[str, int, bool]], Union[str, int, bool]]
+        ] = None,
+        input_mode: Optional[
+            Union[Var[Union[str, int, bool]], Union[str, int, bool]]
+        ] = None,
+        item_prop: Optional[
+            Union[Var[Union[str, int, bool]], Union[str, int, bool]]
+        ] = None,
+        lang: Optional[Union[Var[Union[str, int, bool]], Union[str, int, bool]]] = None,
+        role: Optional[Union[Var[Union[str, int, bool]], Union[str, int, bool]]] = None,
+        slot: Optional[Union[Var[Union[str, int, bool]], Union[str, int, bool]]] = None,
+        spell_check: Optional[
+            Union[Var[Union[str, int, bool]], Union[str, int, bool]]
+        ] = None,
+        tab_index: Optional[
+            Union[Var[Union[str, int, bool]], Union[str, int, bool]]
+        ] = None,
+        title: Optional[
+            Union[Var[Union[str, int, bool]], Union[str, int, bool]]
+        ] = None,
         style: Optional[Style] = None,
         style: Optional[Style] = None,
         key: Optional[Any] = None,
         key: Optional[Any] = None,
         id: Optional[Any] = None,
         id: Optional[Any] = None,
@@ -903,9 +1137,34 @@ class FormNamespace(ComponentNamespace):
 
 
         Args:
         Args:
             *children: The children of the form.
             *children: The children of the form.
+            as_child: Change the default rendered element for the one passed as a child.
+            accept: MIME types the server accepts for file upload
+            accept_charset: Character encodings to be used for form submission
+            action: URL where the form's data should be submitted
+            auto_complete: Whether the form should have autocomplete enabled
+            enc_type: Encoding type for the form data when submitted
+            method: HTTP method to use for form submission
+            name: Name of the form
+            no_validate: Indicates that the form should not be validated on submit
+            target: Where to display the response after submitting the form
             reset_on_submit: If true, the form will be cleared after submit.
             reset_on_submit: If true, the form will be cleared after submit.
             handle_submit_unique_name: The name used to make this form's submit handler function unique.
             handle_submit_unique_name: The name used to make this form's submit handler function unique.
-            as_child: Change the default rendered element for the one passed as a child.
+            access_key:  Provides a hint for generating a keyboard shortcut for the current element.
+            auto_capitalize: Controls whether and how text input is automatically capitalized as it is entered/edited by the user.
+            content_editable: Indicates whether the element's content is editable.
+            context_menu: Defines the ID of a <menu> element which will serve as the element's context menu.
+            dir: Defines the text direction. Allowed values are ltr (Left-To-Right) or rtl (Right-To-Left)
+            draggable: Defines whether the element can be dragged.
+            enter_key_hint: Hints what media types the media element is able to play.
+            hidden: Defines whether the element is hidden.
+            input_mode: Defines the type of the element.
+            item_prop: Defines the name of the element for metadata purposes.
+            lang: Defines the language used in the element.
+            role: Defines the role of the element.
+            slot: Assigns a slot in a shadow DOM shadow tree to an element.
+            spell_check: Defines whether the element may be checked for spelling errors.
+            tab_index: Defines the position of the current element in the tabbing order.
+            title: Defines a tooltip for the element.
             style: The style of the component.
             style: The style of the component.
             key: A unique key for the component.
             key: A unique key for the component.
             id: The id for the component.
             id: The id for the component.