form.py 4.6 KB

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