select.py 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109
  1. """A select component."""
  2. from typing import Any, Dict, List
  3. from pynecone.components.component import EVENT_ARG, Component
  4. from pynecone.components.layout.foreach import Foreach
  5. from pynecone.components.libs.chakra import ChakraComponent
  6. from pynecone.components.typography.text import Text
  7. from pynecone.utils import types
  8. from pynecone.vars import Var
  9. class Select(ChakraComponent):
  10. """Select component is a component that allows users pick a value from predefined options. Ideally, it should be used when there are more than 5 options, otherwise you might consider using a radio group instead."""
  11. tag = "Select"
  12. # State var to bind the select.
  13. value: Var[str]
  14. # The default value of the select.
  15. default_value: Var[str]
  16. # The placeholder text.
  17. placeholder: Var[str]
  18. # The border color when the select is invalid.
  19. error_border_color: Var[str]
  20. # The border color when the select is focused.
  21. focus_border_color: Var[str]
  22. # If true, the select will be disabled.
  23. is_disabled: Var[bool]
  24. # If true, the form control will be invalid. This has 2 side effects: - The FormLabel and FormErrorIcon will have `data-invalid` set to true - The form element (e.g, Input) will have `aria-invalid` set to true
  25. is_invalid: Var[bool]
  26. # If true, the form control will be readonly
  27. is_read_only: Var[bool]
  28. # If true, the form control will be required. This has 2 side effects: - The FormLabel will show a required indicator - The form element (e.g, Input) will have `aria-required` set to true
  29. is_required: Var[bool]
  30. # "outline" | "filled" | "flushed" | "unstyled"
  31. variant: Var[str]
  32. # The size of the select.
  33. size: Var[str]
  34. def get_controlled_triggers(self) -> Dict[str, Var]:
  35. """Get the event triggers that pass the component's value to the handler.
  36. Returns:
  37. A dict mapping the event trigger to the var that is passed to the handler.
  38. """
  39. return {
  40. "on_change": EVENT_ARG.target.value,
  41. }
  42. @classmethod
  43. def create(cls, *children, **props) -> Component:
  44. """Create a select component.
  45. If a list is provided as the first children, a default component
  46. will be created for each item in the list.
  47. Args:
  48. *children: The children of the component.
  49. **props: The props of the component.
  50. Returns:
  51. The component.
  52. """
  53. if len(children) == 1 and isinstance(children[0], list):
  54. children = [Option.create(child) for child in children[0]]
  55. if (
  56. len(children) == 1
  57. and isinstance(children[0], Var)
  58. and types._issubclass(children[0].type_, List)
  59. ):
  60. children = [Foreach.create(children[0], lambda item: Option.create(item))]
  61. return super().create(*children, **props)
  62. class Option(Text):
  63. """A select option."""
  64. tag = "option"
  65. value: Var[Any]
  66. @classmethod
  67. def create(cls, *children, **props) -> Component:
  68. """Create a select option component.
  69. By default, the value of the option is the text of the option.
  70. Args:
  71. *children: The children of the component.
  72. **props: The props of the component.
  73. Returns:
  74. The component.
  75. """
  76. if "value" not in props:
  77. assert len(children) == 1
  78. props["value"] = children[0]
  79. return super().create(*children, **props)