forms.py 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646
  1. """Element classes. This is an auto-generated file. Do not edit. See ../generate.py."""
  2. from __future__ import annotations
  3. from hashlib import md5
  4. from typing import Any, Dict, Iterator, Set, Union
  5. from jinja2 import Environment
  6. from reflex.components.el.element import Element
  7. from reflex.components.tags.tag import Tag
  8. from reflex.constants import Dirs, EventTriggers
  9. from reflex.event import EventChain
  10. from reflex.utils import imports
  11. from reflex.utils.format import format_event_chain
  12. from reflex.vars import BaseVar, Var
  13. from .base import BaseHTML
  14. FORM_DATA = Var.create("form_data")
  15. HANDLE_SUBMIT_JS_JINJA2 = Environment().from_string(
  16. """
  17. const handleSubmit_{{ handle_submit_unique_name }} = useCallback((ev) => {
  18. const $form = ev.target
  19. ev.preventDefault()
  20. const {{ form_data }} = {...Object.fromEntries(new FormData($form).entries()), ...{{ field_ref_mapping }}}
  21. {{ on_submit_event_chain }}
  22. if ({{ reset_on_submit }}) {
  23. $form.reset()
  24. }
  25. })
  26. """
  27. )
  28. class Button(BaseHTML):
  29. """Display the button element."""
  30. tag = "button"
  31. # Automatically focuses the button when the page loads
  32. auto_focus: Var[Union[str, int, bool]]
  33. # Disables the button
  34. disabled: Var[bool]
  35. # Associates the button with a form (by id)
  36. form: Var[Union[str, int, bool]]
  37. # URL to send the form data to (for type="submit" buttons)
  38. form_action: Var[Union[str, int, bool]]
  39. # How the form data should be encoded when submitting to the server (for type="submit" buttons)
  40. form_enc_type: Var[Union[str, int, bool]]
  41. # HTTP method to use for sending form data (for type="submit" buttons)
  42. form_method: Var[Union[str, int, bool]]
  43. # Bypasses form validation when submitting (for type="submit" buttons)
  44. form_no_validate: Var[Union[str, int, bool]]
  45. # Specifies where to display the response after submitting the form (for type="submit" buttons)
  46. form_target: Var[Union[str, int, bool]]
  47. # Name of the button, used when sending form data
  48. name: Var[Union[str, int, bool]]
  49. # Type of the button (submit, reset, or button)
  50. type: Var[Union[str, int, bool]]
  51. # Value of the button, used when sending form data
  52. value: Var[Union[str, int, bool]]
  53. class Datalist(BaseHTML):
  54. """Display the datalist element."""
  55. tag = "datalist"
  56. # No unique attributes, only common ones are inherited
  57. class Fieldset(Element):
  58. """Display the fieldset element."""
  59. tag = "fieldset"
  60. # Disables all the form control descendants of the fieldset
  61. disabled: Var[Union[str, int, bool]]
  62. # Associates the fieldset with a form (by id)
  63. form: Var[Union[str, int, bool]]
  64. # Name of the fieldset, used for scripting
  65. name: Var[Union[str, int, bool]]
  66. class Form(BaseHTML):
  67. """Display the form element."""
  68. tag = "form"
  69. # MIME types the server accepts for file upload
  70. accept: Var[Union[str, int, bool]]
  71. # Character encodings to be used for form submission
  72. accept_charset: Var[Union[str, int, bool]]
  73. # URL where the form's data should be submitted
  74. action: Var[Union[str, int, bool]]
  75. # Whether the form should have autocomplete enabled
  76. auto_complete: Var[Union[str, int, bool]]
  77. # Encoding type for the form data when submitted
  78. enc_type: Var[Union[str, int, bool]]
  79. # HTTP method to use for form submission
  80. method: Var[Union[str, int, bool]]
  81. # Name of the form
  82. name: Var[Union[str, int, bool]]
  83. # Indicates that the form should not be validated on submit
  84. no_validate: Var[Union[str, int, bool]]
  85. # Where to display the response after submitting the form
  86. target: Var[Union[str, int, bool]]
  87. # If true, the form will be cleared after submit.
  88. reset_on_submit: Var[bool] = False # type: ignore
  89. # The name used to make this form's submit handler function unique.
  90. handle_submit_unique_name: Var[str]
  91. def get_event_triggers(self) -> Dict[str, Any]:
  92. """Event triggers for radix form root.
  93. Returns:
  94. The triggers for event supported by Root.
  95. """
  96. return {
  97. **super().get_event_triggers(),
  98. EventTriggers.ON_SUBMIT: lambda e0: [FORM_DATA],
  99. }
  100. @classmethod
  101. def create(cls, *children, **props):
  102. """Create a form component.
  103. Args:
  104. *children: The children of the form.
  105. **props: The properties of the form.
  106. Returns:
  107. The form component.
  108. """
  109. if "handle_submit_unique_name" in props:
  110. return super().create(*children, **props)
  111. # Render the form hooks and use the hash of the resulting code to create a unique name.
  112. props["handle_submit_unique_name"] = ""
  113. form = super().create(*children, **props)
  114. form.handle_submit_unique_name = md5(
  115. str({**form._get_all_hooks_internal(), **form._get_all_hooks()}).encode(
  116. "utf-8"
  117. )
  118. ).hexdigest()
  119. return form
  120. def _get_imports(self) -> imports.ImportDict:
  121. return imports.merge_imports(
  122. super()._get_imports(),
  123. {
  124. "react": {imports.ImportVar(tag="useCallback")},
  125. f"/{Dirs.STATE_PATH}": {
  126. imports.ImportVar(tag="getRefValue"),
  127. imports.ImportVar(tag="getRefValues"),
  128. },
  129. },
  130. )
  131. def _get_hooks(self) -> str | None:
  132. if EventTriggers.ON_SUBMIT not in self.event_triggers:
  133. return
  134. return HANDLE_SUBMIT_JS_JINJA2.render(
  135. handle_submit_unique_name=self.handle_submit_unique_name,
  136. form_data=FORM_DATA,
  137. field_ref_mapping=str(Var.create_safe(self._get_form_refs())),
  138. on_submit_event_chain=format_event_chain(
  139. self.event_triggers[EventTriggers.ON_SUBMIT]
  140. ),
  141. reset_on_submit=self.reset_on_submit,
  142. )
  143. def _render(self) -> Tag:
  144. render_tag = super()._render()
  145. if EventTriggers.ON_SUBMIT in self.event_triggers:
  146. render_tag.add_props(
  147. **{
  148. EventTriggers.ON_SUBMIT: BaseVar(
  149. _var_name=f"handleSubmit_{self.handle_submit_unique_name}",
  150. _var_type=EventChain,
  151. )
  152. }
  153. )
  154. return render_tag
  155. def _get_form_refs(self) -> Dict[str, Any]:
  156. # Send all the input refs to the handler.
  157. form_refs = {}
  158. for ref in self._get_all_refs():
  159. # when ref start with refs_ it's an array of refs, so we need different method
  160. # to collect data
  161. if ref.startswith("refs_"):
  162. ref_var = Var.create_safe(ref[:-3]).as_ref()
  163. form_refs[ref[5:-3]] = Var.create_safe(
  164. f"getRefValues({str(ref_var)})", _var_is_local=False
  165. )._replace(merge_var_data=ref_var._var_data)
  166. else:
  167. ref_var = Var.create_safe(ref).as_ref()
  168. form_refs[ref[4:]] = Var.create_safe(
  169. f"getRefValue({str(ref_var)})", _var_is_local=False
  170. )._replace(merge_var_data=ref_var._var_data)
  171. return form_refs
  172. def _get_vars(self, include_children: bool = True) -> Iterator[Var]:
  173. yield from super()._get_vars(include_children=include_children)
  174. yield from self._get_form_refs().values()
  175. def _exclude_props(self) -> list[str]:
  176. return super()._exclude_props() + [
  177. "reset_on_submit",
  178. "handle_submit_unique_name",
  179. ]
  180. class Input(BaseHTML):
  181. """Display the input element."""
  182. tag = "input"
  183. # Accepted types of files when the input is file type
  184. accept: Var[Union[str, int, bool]]
  185. # Alternate text for input type="image"
  186. alt: Var[Union[str, int, bool]]
  187. # Whether the input should have autocomplete enabled
  188. auto_complete: Var[Union[str, int, bool]]
  189. # Automatically focuses the input when the page loads
  190. auto_focus: Var[Union[str, int, bool]]
  191. # Captures media from the user (camera or microphone)
  192. capture: Var[Union[str, int, bool]]
  193. # Indicates whether the input is checked (for checkboxes and radio buttons)
  194. checked: Var[Union[str, int, bool]]
  195. # The initial value (for checkboxes and radio buttons)
  196. default_checked: Var[bool]
  197. # The initial value for a text field
  198. default_value: Var[str]
  199. # Name part of the input to submit in 'dir' and 'name' pair when form is submitted
  200. dirname: Var[Union[str, int, bool]]
  201. # Disables the input
  202. disabled: Var[Union[str, int, bool]]
  203. # Associates the input with a form (by id)
  204. form: Var[Union[str, int, bool]]
  205. # URL to send the form data to (for type="submit" buttons)
  206. form_action: Var[Union[str, int, bool]]
  207. # How the form data should be encoded when submitting to the server (for type="submit" buttons)
  208. form_enc_type: Var[Union[str, int, bool]]
  209. # HTTP method to use for sending form data (for type="submit" buttons)
  210. form_method: Var[Union[str, int, bool]]
  211. # Bypasses form validation when submitting (for type="submit" buttons)
  212. form_no_validate: Var[Union[str, int, bool]]
  213. # Specifies where to display the response after submitting the form (for type="submit" buttons)
  214. form_target: Var[Union[str, int, bool]]
  215. # References a datalist for suggested options
  216. list: Var[Union[str, int, bool]]
  217. # Specifies the maximum value for the input
  218. max: Var[Union[str, int, bool]]
  219. # Specifies the maximum number of characters allowed in the input
  220. max_length: Var[Union[str, int, bool]]
  221. # Specifies the minimum number of characters required in the input
  222. min_length: Var[Union[str, int, bool]]
  223. # Specifies the minimum value for the input
  224. min: Var[Union[str, int, bool]]
  225. # Indicates whether multiple values can be entered in an input of the type email or file
  226. multiple: Var[Union[str, int, bool]]
  227. # Name of the input, used when sending form data
  228. name: Var[Union[str, int, bool]]
  229. # Regex pattern the input's value must match to be valid
  230. pattern: Var[Union[str, int, bool]]
  231. # Placeholder text in the input
  232. placeholder: Var[Union[str, int, bool]]
  233. # Indicates whether the input is read-only
  234. read_only: Var[Union[str, int, bool]]
  235. # Indicates that the input is required
  236. required: Var[Union[str, int, bool]]
  237. # Specifies the visible width of a text control
  238. size: Var[Union[str, int, bool]]
  239. # URL for image inputs
  240. src: Var[Union[str, int, bool]]
  241. # Specifies the legal number intervals for an input
  242. step: Var[Union[str, int, bool]]
  243. # Specifies the type of input
  244. type: Var[Union[str, int, bool]]
  245. # Name of the image map used with the input
  246. use_map: Var[Union[str, int, bool]]
  247. # Value of the input
  248. value: Var[Union[str, int, bool]]
  249. def get_event_triggers(self) -> Dict[str, Any]:
  250. """Get the event triggers that pass the component's value to the handler.
  251. Returns:
  252. A dict mapping the event trigger to the var that is passed to the handler.
  253. """
  254. return {
  255. **super().get_event_triggers(),
  256. EventTriggers.ON_CHANGE: lambda e0: [e0.target.value],
  257. EventTriggers.ON_FOCUS: lambda e0: [e0.target.value],
  258. EventTriggers.ON_BLUR: lambda e0: [e0.target.value],
  259. EventTriggers.ON_KEY_DOWN: lambda e0: [e0.key],
  260. EventTriggers.ON_KEY_UP: lambda e0: [e0.key],
  261. }
  262. class Label(BaseHTML):
  263. """Display the label element."""
  264. tag = "label"
  265. # ID of a form control with which the label is associated
  266. html_for: Var[Union[str, int, bool]]
  267. # Associates the label with a form (by id)
  268. form: Var[Union[str, int, bool]]
  269. class Legend(BaseHTML):
  270. """Display the legend element."""
  271. tag = "legend"
  272. # No unique attributes, only common ones are inherited
  273. class Meter(BaseHTML):
  274. """Display the meter element."""
  275. tag = "meter"
  276. # Associates the meter with a form (by id)
  277. form: Var[Union[str, int, bool]]
  278. # High limit of range (above this is considered high value)
  279. high: Var[Union[str, int, bool]]
  280. # Low limit of range (below this is considered low value)
  281. low: Var[Union[str, int, bool]]
  282. # Maximum value of the range
  283. max: Var[Union[str, int, bool]]
  284. # Minimum value of the range
  285. min: Var[Union[str, int, bool]]
  286. # Optimum value in the range
  287. optimum: Var[Union[str, int, bool]]
  288. # Current value of the meter
  289. value: Var[Union[str, int, bool]]
  290. class Optgroup(BaseHTML):
  291. """Display the optgroup element."""
  292. tag = "optgroup"
  293. # Disables the optgroup
  294. disabled: Var[Union[str, int, bool]]
  295. # Label for the optgroup
  296. label: Var[Union[str, int, bool]]
  297. class Option(BaseHTML):
  298. """Display the option element."""
  299. tag = "option"
  300. # Disables the option
  301. disabled: Var[Union[str, int, bool]]
  302. # Label for the option, if the text is not the label
  303. label: Var[Union[str, int, bool]]
  304. # Indicates that the option is initially selected
  305. selected: Var[Union[str, int, bool]]
  306. # Value to be sent as form data
  307. value: Var[Union[str, int, bool]]
  308. class Output(BaseHTML):
  309. """Display the output element."""
  310. tag = "output"
  311. # Associates the output with one or more elements (by their IDs)
  312. html_for: Var[Union[str, int, bool]]
  313. # Associates the output with a form (by id)
  314. form: Var[Union[str, int, bool]]
  315. # Name of the output element for form submission
  316. name: Var[Union[str, int, bool]]
  317. class Progress(BaseHTML):
  318. """Display the progress element."""
  319. tag = "progress"
  320. # Associates the progress element with a form (by id)
  321. form: Var[Union[str, int, bool]]
  322. # Maximum value of the progress indicator
  323. max: Var[Union[str, int, bool]]
  324. # Current value of the progress indicator
  325. value: Var[Union[str, int, bool]]
  326. class Select(BaseHTML):
  327. """Display the select element."""
  328. tag = "select"
  329. # Whether the form control should have autocomplete enabled
  330. auto_complete: Var[Union[str, int, bool]]
  331. # Automatically focuses the select when the page loads
  332. auto_focus: Var[Union[str, int, bool]]
  333. # Disables the select control
  334. disabled: Var[Union[str, int, bool]]
  335. # Associates the select with a form (by id)
  336. form: Var[Union[str, int, bool]]
  337. # Indicates that multiple options can be selected
  338. multiple: Var[Union[str, int, bool]]
  339. # Name of the select, used when submitting the form
  340. name: Var[Union[str, int, bool]]
  341. # Indicates that the select control must have a selected option
  342. required: Var[Union[str, int, bool]]
  343. # Number of visible options in a drop-down list
  344. size: Var[Union[str, int, bool]]
  345. def get_event_triggers(self) -> Dict[str, Any]:
  346. """Get the event triggers that pass the component's value to the handler.
  347. Returns:
  348. A dict mapping the event trigger to the var that is passed to the handler.
  349. """
  350. return {
  351. **super().get_event_triggers(),
  352. EventTriggers.ON_CHANGE: lambda e0: [e0.target.value],
  353. }
  354. AUTO_HEIGHT_JS = """
  355. const autoHeightOnInput = (e, is_enabled) => {
  356. if (is_enabled) {
  357. const el = e.target;
  358. el.style.overflowY = "hidden";
  359. el.style.height = "auto";
  360. el.style.height = (e.target.scrollHeight) + "px";
  361. if (el.form && !el.form.data_resize_on_reset) {
  362. el.form.addEventListener("reset", () => window.setTimeout(() => autoHeightOnInput(e, is_enabled), 0))
  363. el.form.data_resize_on_reset = true;
  364. }
  365. }
  366. }
  367. """
  368. ENTER_KEY_SUBMIT_JS = """
  369. const enterKeySubmitOnKeyDown = (e, is_enabled) => {
  370. if (is_enabled && e.which === 13 && !e.shiftKey) {
  371. e.preventDefault();
  372. if (!e.repeat) {
  373. if (e.target.form) {
  374. e.target.form.requestSubmit();
  375. }
  376. }
  377. }
  378. }
  379. """
  380. class Textarea(BaseHTML):
  381. """Display the textarea element."""
  382. tag = "textarea"
  383. # Whether the form control should have autocomplete enabled
  384. auto_complete: Var[Union[str, int, bool]]
  385. # Automatically focuses the textarea when the page loads
  386. auto_focus: Var[Union[str, int, bool]]
  387. # Automatically fit the content height to the text (use min-height with this prop)
  388. auto_height: Var[bool]
  389. # Visible width of the text control, in average character widths
  390. cols: Var[Union[str, int, bool]]
  391. # Name part of the textarea to submit in 'dir' and 'name' pair when form is submitted
  392. dirname: Var[Union[str, int, bool]]
  393. # Disables the textarea
  394. disabled: Var[Union[str, int, bool]]
  395. # Enter key submits form (shift-enter adds new line)
  396. enter_key_submit: Var[bool]
  397. # Associates the textarea with a form (by id)
  398. form: Var[Union[str, int, bool]]
  399. # Maximum number of characters allowed in the textarea
  400. max_length: Var[Union[str, int, bool]]
  401. # Minimum number of characters required in the textarea
  402. min_length: Var[Union[str, int, bool]]
  403. # Name of the textarea, used when submitting the form
  404. name: Var[Union[str, int, bool]]
  405. # Placeholder text in the textarea
  406. placeholder: Var[Union[str, int, bool]]
  407. # Indicates whether the textarea is read-only
  408. read_only: Var[Union[str, int, bool]]
  409. # Indicates that the textarea is required
  410. required: Var[Union[str, int, bool]]
  411. # Visible number of lines in the text control
  412. rows: Var[Union[str, int, bool]]
  413. # The controlled value of the textarea, read only unless used with on_change
  414. value: Var[Union[str, int, bool]]
  415. # How the text in the textarea is to be wrapped when submitting the form
  416. wrap: Var[Union[str, int, bool]]
  417. def _exclude_props(self) -> list[str]:
  418. return super()._exclude_props() + [
  419. "auto_height",
  420. "enter_key_submit",
  421. ]
  422. def _get_all_custom_code(self) -> Set[str]:
  423. """Include the custom code for auto_height and enter_key_submit functionality.
  424. Returns:
  425. The custom code for the component.
  426. """
  427. custom_code = super()._get_all_custom_code()
  428. if self.auto_height is not None:
  429. custom_code.add(AUTO_HEIGHT_JS)
  430. if self.enter_key_submit is not None:
  431. custom_code.add(ENTER_KEY_SUBMIT_JS)
  432. return custom_code
  433. def _render(self) -> Tag:
  434. tag = super()._render()
  435. if self.enter_key_submit is not None:
  436. if "on_key_down" in self.event_triggers:
  437. raise ValueError(
  438. "Cannot combine `enter_key_submit` with `on_key_down`.",
  439. )
  440. tag.add_props(
  441. on_key_down=Var.create_safe(
  442. f"(e) => enterKeySubmitOnKeyDown(e, {self.enter_key_submit._var_name_unwrapped})",
  443. _var_is_local=False,
  444. )._replace(merge_var_data=self.enter_key_submit._var_data),
  445. )
  446. if self.auto_height is not None:
  447. tag.add_props(
  448. on_input=Var.create_safe(
  449. f"(e) => autoHeightOnInput(e, {self.auto_height._var_name_unwrapped})",
  450. _var_is_local=False,
  451. )._replace(merge_var_data=self.auto_height._var_data),
  452. )
  453. return tag
  454. def get_event_triggers(self) -> Dict[str, Any]:
  455. """Get the event triggers that pass the component's value to the handler.
  456. Returns:
  457. A dict mapping the event trigger to the var that is passed to the handler.
  458. """
  459. return {
  460. **super().get_event_triggers(),
  461. EventTriggers.ON_CHANGE: lambda e0: [e0.target.value],
  462. EventTriggers.ON_FOCUS: lambda e0: [e0.target.value],
  463. EventTriggers.ON_BLUR: lambda e0: [e0.target.value],
  464. EventTriggers.ON_KEY_DOWN: lambda e0: [e0.key],
  465. EventTriggers.ON_KEY_UP: lambda e0: [e0.key],
  466. }