upload.py 3.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108
  1. """A file upload component."""
  2. from __future__ import annotations
  3. from typing import Dict, List, Optional
  4. from reflex.components.component import EVENT_ARG, Component
  5. from reflex.components.forms.input import Input
  6. from reflex.components.layout.box import Box
  7. from reflex.event import EventChain
  8. from reflex.vars import BaseVar, Var
  9. files_state: str = "const [files, setFiles] = useState([]);"
  10. upload_file: BaseVar = BaseVar(name="e => setFiles((files) => e)", type_=EventChain)
  11. # Use this var along with the Upload component to render the list of selected files.
  12. selected_files: BaseVar = BaseVar(name="files.map((f) => f.name)", type_=List[str])
  13. clear_selected_files: BaseVar = BaseVar(
  14. name="_e => setFiles((files) => [])", type_=EventChain
  15. )
  16. class Upload(Component):
  17. """A file upload component."""
  18. library = "react-dropzone@^14.2.3"
  19. tag = "ReactDropzone"
  20. is_default = True
  21. # The list of accepted file types. This should be a dictionary of MIME types as keys and array of file formats as
  22. # values.
  23. # supported MIME types: https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/MIME_types/Common_types
  24. accept: Var[Optional[Dict[str, List]]]
  25. # Whether the dropzone is disabled.
  26. disabled: Var[bool]
  27. # The maximum number of files that can be uploaded.
  28. max_files: Var[int]
  29. # The maximum file size (bytes) that can be uploaded.
  30. max_size: Var[int]
  31. # The minimum file size (bytes) that can be uploaded.
  32. min_size: Var[int]
  33. # Whether to allow multiple files to be uploaded.
  34. multiple: Var[bool] = True # type: ignore
  35. # Whether to disable click to upload.
  36. no_click: Var[bool]
  37. # Whether to disable drag and drop.
  38. no_drag: Var[bool]
  39. # Whether to disable using the space/enter keys to upload.
  40. no_keyboard: Var[bool]
  41. @classmethod
  42. def create(cls, *children, **props) -> Component:
  43. """Create an upload component.
  44. Args:
  45. *children: The children of the component.
  46. **props: The properties of the component.
  47. Returns:
  48. The upload component.
  49. """
  50. # get only upload component props
  51. supported_props = cls.get_props()
  52. upload_props = {
  53. key: value for key, value in props.items() if key in supported_props
  54. }
  55. # The file input to use.
  56. upload = Input.create(type_="file")
  57. upload.special_props = {BaseVar(name="{...getInputProps()}", type_=None)}
  58. # The dropzone to use.
  59. zone = Box.create(
  60. upload,
  61. *children,
  62. **{k: v for k, v in props.items() if k not in supported_props},
  63. )
  64. zone.special_props = {BaseVar(name="{...getRootProps()}", type_=None)}
  65. # Create the component.
  66. return super().create(zone, on_drop=upload_file, **upload_props)
  67. def get_controlled_triggers(self) -> Dict[str, Var]:
  68. """Get the event triggers that pass the component's value to the handler.
  69. Returns:
  70. A dict mapping the event trigger to the var that is passed to the handler.
  71. """
  72. return {
  73. "on_drop": EVENT_ARG,
  74. }
  75. def _render(self):
  76. out = super()._render()
  77. out.args = ("getRootProps", "getInputProps")
  78. return out
  79. def _get_hooks(self) -> str | None:
  80. return (super()._get_hooks() or "") + files_state