form.py 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195
  1. """Radix form component."""
  2. from __future__ import annotations
  3. from typing import Any, Dict, Literal
  4. from reflex.components.component import ComponentNamespace
  5. from reflex.components.el.elements.forms import Form as HTMLForm
  6. from reflex.components.radix.themes.components.text_field import TextFieldRoot
  7. from reflex.constants.event import EventTriggers
  8. from reflex.style import Style
  9. from reflex.vars import Var
  10. from .base import RadixPrimitiveComponentWithClassName
  11. class FormComponent(RadixPrimitiveComponentWithClassName):
  12. """Base class for all @radix-ui/react-form components."""
  13. library = "@radix-ui/react-form@^0.0.3"
  14. class FormRoot(FormComponent, HTMLForm):
  15. """The root component of a radix form."""
  16. tag = "Root"
  17. alias = "RadixFormRoot"
  18. def get_event_triggers(self) -> Dict[str, Any]:
  19. """Event triggers for radix form root.
  20. Returns:
  21. The triggers for event supported by Root.
  22. """
  23. return {
  24. **super().get_event_triggers(),
  25. EventTriggers.ON_CLEAR_SERVER_ERRORS: lambda: [],
  26. }
  27. def add_style(self) -> Style | None:
  28. """Add style to the component.
  29. Returns:
  30. The style of the component.
  31. """
  32. return Style({"width": "100%"})
  33. class FormField(FormComponent):
  34. """A form field component."""
  35. tag = "Field"
  36. alias = "RadixFormField"
  37. # The name of the form field, that is passed down to the control and used to match with validation messages.
  38. name: Var[str]
  39. # Flag to mark the form field as invalid, for server side validation.
  40. server_invalid: Var[bool]
  41. def add_style(self) -> Style | None:
  42. """Add style to the component.
  43. Returns:
  44. The style of the component.
  45. """
  46. return Style({"display": "grid", "margin_bottom": "10px"})
  47. class FormLabel(FormComponent):
  48. """A form label component."""
  49. tag = "Label"
  50. alias = "RadixFormLabel"
  51. def add_style(self) -> Style | None:
  52. """Add style to the component.
  53. Returns:
  54. The style of the component.
  55. """
  56. return Style({"font_size": "15px", "font_weight": "500", "line_height": "35px"})
  57. class FormControl(FormComponent):
  58. """A form control component."""
  59. tag = "Control"
  60. alias = "RadixFormControl"
  61. @classmethod
  62. def create(cls, *children, **props):
  63. """Create a Form Control component.
  64. Args:
  65. *children: The children of the form.
  66. **props: The properties of the form.
  67. Raises:
  68. ValueError: If the number of children is greater than 1.
  69. TypeError: If a child exists but it is not a TextFieldInput.
  70. Returns:
  71. The form control component.
  72. """
  73. if len(children) > 1:
  74. raise ValueError(
  75. f"FormControl can only have at most one child, got {len(children)} children"
  76. )
  77. for child in children:
  78. if not isinstance(child, TextFieldRoot):
  79. raise TypeError(
  80. "Only Radix TextFieldRoot is allowed as child of FormControl"
  81. )
  82. return super().create(*children, **props)
  83. LiteralMatcher = Literal[
  84. "badInput",
  85. "patternMismatch",
  86. "rangeOverflow",
  87. "rangeUnderflow",
  88. "stepMismatch",
  89. "tooLong",
  90. "tooShort",
  91. "typeMismatch",
  92. "valid",
  93. "valueMissing",
  94. ]
  95. class FormMessage(FormComponent):
  96. """A form message component."""
  97. tag = "Message"
  98. alias = "RadixFormMessage"
  99. # Used to target a specific field by name when rendering outside of a Field part.
  100. name: Var[str]
  101. # Used to indicate on which condition the message should be visible.
  102. match: Var[LiteralMatcher]
  103. # Forces the message to be shown. This is useful when using server-side validation.
  104. force_match: Var[bool]
  105. def add_style(self) -> Style | None:
  106. """Add style to the component.
  107. Returns:
  108. The style of the component.
  109. """
  110. return Style({"font_size": "13px", "opacity": "0.8", "color": "white"})
  111. class FormValidityState(FormComponent):
  112. """A form validity state component."""
  113. tag = "ValidityState"
  114. alias = "RadixFormValidityState"
  115. class FormSubmit(FormComponent):
  116. """A form submit component."""
  117. tag = "Submit"
  118. alias = "RadixFormSubmit"
  119. # This class is created mainly for reflex-web docs.
  120. class Form(FormRoot):
  121. """The Form component."""
  122. pass
  123. class FormNamespace(ComponentNamespace):
  124. """Form components."""
  125. root = staticmethod(FormRoot.create)
  126. control = staticmethod(FormControl.create)
  127. field = staticmethod(FormField.create)
  128. label = staticmethod(FormLabel.create)
  129. message = staticmethod(FormMessage.create)
  130. submit = staticmethod(FormSubmit.create)
  131. validity_state = staticmethod(FormValidityState.create)
  132. __call__ = staticmethod(Form.create)
  133. form = FormNamespace()