pininput.py 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143
  1. """A pin input component."""
  2. from typing import Dict, Optional
  3. from reflex.components.component import Component
  4. from reflex.components.layout import Foreach
  5. from reflex.components.libs.chakra import ChakraComponent
  6. from reflex.event import EVENT_ARG
  7. from reflex.utils import format
  8. from reflex.vars import Var
  9. class PinInput(ChakraComponent):
  10. """The component that provides context to all the pin-input fields."""
  11. tag = "PinInput"
  12. # State var to bind the the input.
  13. value: Var[str]
  14. # If true, the pin input receives focus on mount
  15. auto_focus: Var[bool]
  16. # The default value of the pin input
  17. default_value: Var[str]
  18. # The border color when the input is invalid.
  19. error_border_color: Var[str]
  20. # The border color when the input is focused.
  21. focus_border_color: Var[str]
  22. # The top-level id string that will be applied to the input fields. The index of the input will be appended to this top-level id.
  23. id_: Var[str]
  24. # The length of the number input.
  25. length: Var[int]
  26. # If true, the pin input component is put in the disabled state
  27. is_disabled: Var[bool]
  28. # If true, the pin input component is put in the invalid state
  29. is_invalid: Var[bool]
  30. # If true, focus will move automatically to the next input once filled
  31. manage_focus: Var[bool]
  32. # If true, the input's value will be masked just like `type=password`
  33. mask: Var[bool]
  34. # The placeholder for the pin input
  35. placeholder: Var[str]
  36. # The type of values the pin-input should allow ("number" | "alphanumeric").
  37. type_: Var[str]
  38. # "outline" | "flushed" | "filled" | "unstyled"
  39. variant: Var[str]
  40. def get_controlled_triggers(self) -> Dict[str, Var]:
  41. """Get the event triggers that pass the component's value to the handler.
  42. Returns:
  43. A dict mapping the event trigger to the var that is passed to the handler.
  44. """
  45. return {
  46. "on_change": EVENT_ARG,
  47. "on_complete": EVENT_ARG,
  48. }
  49. def get_ref(self):
  50. """Return a reference because we actually attached the ref to the PinInputFields.
  51. Returns:
  52. None.
  53. """
  54. return None
  55. def _get_ref_hook(self) -> Optional[str]:
  56. """Override the base _get_ref_hook to handle array refs.
  57. Returns:
  58. The overrided hooks.
  59. """
  60. if self.id:
  61. ref = format.format_array_ref(self.id, None)
  62. if ref:
  63. return f"const {ref} = Array.from({{length:{self.length}}}, () => useRef(null));"
  64. return super()._get_ref_hook()
  65. @classmethod
  66. def create(cls, *children, **props) -> Component:
  67. """Create a pin input component.
  68. If no children are passed in, the component will create a default pin input
  69. based on the length prop.
  70. Args:
  71. *children: The children of the component.
  72. **props: The props of the component.
  73. Returns:
  74. The pin input component.
  75. """
  76. if not children and "length" in props:
  77. _id = props.get("id", None)
  78. length = props["length"]
  79. if _id:
  80. children = [
  81. Foreach.create(
  82. list(range(length)), # type: ignore
  83. lambda ref, i: PinInputField.create(
  84. key=i,
  85. id=_id,
  86. index=i,
  87. ),
  88. )
  89. ]
  90. else:
  91. children = [PinInputField()] * length
  92. return super().create(*children, **props)
  93. class PinInputField(ChakraComponent):
  94. """The text field that user types in - must be a direct child of PinInput."""
  95. tag = "PinInputField"
  96. # the position of the PinInputField inside the PinInput.
  97. # Default to None because it is assigned by PinInput when created.
  98. index: Optional[Var[int]] = None
  99. def _get_ref_hook(self) -> Optional[str]:
  100. return None
  101. def get_ref(self):
  102. """Get the array ref for the pin input.
  103. Returns:
  104. The array ref.
  105. """
  106. if self.id:
  107. return format.format_array_ref(self.id, self.index)