textarea: expose auto_height and enter_key_submit props (#2884)
* textarea: expose auto_height and enter_key_submit props These two props improve the workflow for chat apps and other situations where we want multiline input. auto_height: resize the textarea based on its contents enter_key_submit: pressing enter submits the enclosing form (shift+enter inserts new lines) Fix #1231 * Update pyi
This commit is contained in:
parent
68c56a9811
commit
8ef193809c
@ -2,7 +2,7 @@
|
|||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
from hashlib import md5
|
from hashlib import md5
|
||||||
from typing import Any, Dict, Iterator, Union
|
from typing import Any, Dict, Iterator, Set, Union
|
||||||
|
|
||||||
from jinja2 import Environment
|
from jinja2 import Environment
|
||||||
|
|
||||||
@ -500,6 +500,36 @@ class Select(BaseHTML):
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
AUTO_HEIGHT_JS = """
|
||||||
|
const autoHeightOnInput = (e, is_enabled) => {
|
||||||
|
if (is_enabled) {
|
||||||
|
const el = e.target;
|
||||||
|
el.style.overflowY = "hidden";
|
||||||
|
el.style.height = "auto";
|
||||||
|
el.style.height = (e.target.scrollHeight) + "px";
|
||||||
|
if (el.form && !el.form.data_resize_on_reset) {
|
||||||
|
el.form.addEventListener("reset", () => window.setTimeout(() => autoHeightOnInput(e, is_enabled), 0))
|
||||||
|
el.form.data_resize_on_reset = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
ENTER_KEY_SUBMIT_JS = """
|
||||||
|
const enterKeySubmitOnKeyDown = (e, is_enabled) => {
|
||||||
|
if (is_enabled && e.which === 13 && !e.shiftKey) {
|
||||||
|
e.preventDefault();
|
||||||
|
if (!e.repeat) {
|
||||||
|
if (e.target.form) {
|
||||||
|
e.target.form.requestSubmit();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
class Textarea(BaseHTML):
|
class Textarea(BaseHTML):
|
||||||
"""Display the textarea element."""
|
"""Display the textarea element."""
|
||||||
|
|
||||||
@ -511,6 +541,9 @@ class Textarea(BaseHTML):
|
|||||||
# Automatically focuses the textarea when the page loads
|
# Automatically focuses the textarea when the page loads
|
||||||
auto_focus: Var[Union[str, int, bool]]
|
auto_focus: Var[Union[str, int, bool]]
|
||||||
|
|
||||||
|
# Automatically fit the content height to the text (use min-height with this prop)
|
||||||
|
auto_height: Var[bool]
|
||||||
|
|
||||||
# Visible width of the text control, in average character widths
|
# Visible width of the text control, in average character widths
|
||||||
cols: Var[Union[str, int, bool]]
|
cols: Var[Union[str, int, bool]]
|
||||||
|
|
||||||
@ -520,6 +553,9 @@ class Textarea(BaseHTML):
|
|||||||
# Disables the textarea
|
# Disables the textarea
|
||||||
disabled: Var[Union[str, int, bool]]
|
disabled: Var[Union[str, int, bool]]
|
||||||
|
|
||||||
|
# Enter key submits form (shift-enter adds new line)
|
||||||
|
enter_key_submit: Var[bool]
|
||||||
|
|
||||||
# Associates the textarea with a form (by id)
|
# Associates the textarea with a form (by id)
|
||||||
form: Var[Union[str, int, bool]]
|
form: Var[Union[str, int, bool]]
|
||||||
|
|
||||||
@ -550,6 +586,47 @@ class Textarea(BaseHTML):
|
|||||||
# How the text in the textarea is to be wrapped when submitting the form
|
# How the text in the textarea is to be wrapped when submitting the form
|
||||||
wrap: Var[Union[str, int, bool]]
|
wrap: Var[Union[str, int, bool]]
|
||||||
|
|
||||||
|
def _exclude_props(self) -> list[str]:
|
||||||
|
return super()._exclude_props() + [
|
||||||
|
"auto_height",
|
||||||
|
"enter_key_submit",
|
||||||
|
]
|
||||||
|
|
||||||
|
def get_custom_code(self) -> Set[str]:
|
||||||
|
"""Include the custom code for auto_height and enter_key_submit functionality.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
The custom code for the component.
|
||||||
|
"""
|
||||||
|
custom_code = super().get_custom_code()
|
||||||
|
if self.auto_height is not None:
|
||||||
|
custom_code.add(AUTO_HEIGHT_JS)
|
||||||
|
if self.enter_key_submit is not None:
|
||||||
|
custom_code.add(ENTER_KEY_SUBMIT_JS)
|
||||||
|
return custom_code
|
||||||
|
|
||||||
|
def _render(self) -> Tag:
|
||||||
|
tag = super()._render()
|
||||||
|
if self.enter_key_submit is not None:
|
||||||
|
if "on_key_down" in self.event_triggers:
|
||||||
|
raise ValueError(
|
||||||
|
"Cannot combine `enter_key_submit` with `on_key_down`.",
|
||||||
|
)
|
||||||
|
tag.add_props(
|
||||||
|
on_key_down=Var.create_safe(
|
||||||
|
f"(e) => enterKeySubmitOnKeyDown(e, {self.enter_key_submit._var_name_unwrapped})",
|
||||||
|
_var_is_local=False,
|
||||||
|
)._replace(merge_var_data=self.enter_key_submit._var_data),
|
||||||
|
)
|
||||||
|
if self.auto_height is not None:
|
||||||
|
tag.add_props(
|
||||||
|
on_input=Var.create_safe(
|
||||||
|
f"(e) => autoHeightOnInput(e, {self.auto_height._var_name_unwrapped})",
|
||||||
|
_var_is_local=False,
|
||||||
|
)._replace(merge_var_data=self.auto_height._var_data),
|
||||||
|
)
|
||||||
|
return tag
|
||||||
|
|
||||||
def get_event_triggers(self) -> Dict[str, Any]:
|
def get_event_triggers(self) -> Dict[str, Any]:
|
||||||
"""Get the event triggers that pass the component's value to the handler.
|
"""Get the event triggers that pass the component's value to the handler.
|
||||||
|
|
||||||
|
@ -8,7 +8,7 @@ from reflex.vars import Var, BaseVar, ComputedVar
|
|||||||
from reflex.event import EventChain, EventHandler, EventSpec
|
from reflex.event import EventChain, EventHandler, EventSpec
|
||||||
from reflex.style import Style
|
from reflex.style import Style
|
||||||
from hashlib import md5
|
from hashlib import md5
|
||||||
from typing import Any, Dict, Iterator, Union
|
from typing import Any, Dict, Iterator, Set, Union
|
||||||
from jinja2 import Environment
|
from jinja2 import Environment
|
||||||
from reflex.components.el.element import Element
|
from reflex.components.el.element import Element
|
||||||
from reflex.components.tags.tag import Tag
|
from reflex.components.tags.tag import Tag
|
||||||
@ -2018,7 +2018,11 @@ class Select(BaseHTML):
|
|||||||
"""
|
"""
|
||||||
...
|
...
|
||||||
|
|
||||||
|
AUTO_HEIGHT_JS = '\nconst autoHeightOnInput = (e, is_enabled) => {\n if (is_enabled) {\n const el = e.target;\n el.style.overflowY = "hidden";\n el.style.height = "auto";\n el.style.height = (e.target.scrollHeight) + "px";\n if (el.form && !el.form.data_resize_on_reset) {\n el.form.addEventListener("reset", () => window.setTimeout(() => autoHeightOnInput(e, is_enabled), 0))\n el.form.data_resize_on_reset = true;\n }\n }\n}\n'
|
||||||
|
ENTER_KEY_SUBMIT_JS = "\nconst enterKeySubmitOnKeyDown = (e, is_enabled) => {\n if (is_enabled && e.which === 13 && !e.shiftKey) {\n e.preventDefault();\n if (!e.repeat) {\n if (e.target.form) {\n e.target.form.requestSubmit();\n }\n }\n }\n}\n"
|
||||||
|
|
||||||
class Textarea(BaseHTML):
|
class Textarea(BaseHTML):
|
||||||
|
def get_custom_code(self) -> Set[str]: ...
|
||||||
def get_event_triggers(self) -> Dict[str, Any]: ...
|
def get_event_triggers(self) -> Dict[str, Any]: ...
|
||||||
@overload
|
@overload
|
||||||
@classmethod
|
@classmethod
|
||||||
@ -2031,6 +2035,7 @@ class Textarea(BaseHTML):
|
|||||||
auto_focus: Optional[
|
auto_focus: Optional[
|
||||||
Union[Var[Union[str, int, bool]], Union[str, int, bool]]
|
Union[Var[Union[str, int, bool]], Union[str, int, bool]]
|
||||||
] = None,
|
] = None,
|
||||||
|
auto_height: Optional[Union[Var[bool], bool]] = None,
|
||||||
cols: Optional[Union[Var[Union[str, int, bool]], Union[str, int, bool]]] = None,
|
cols: Optional[Union[Var[Union[str, int, bool]], Union[str, int, bool]]] = None,
|
||||||
dirname: Optional[
|
dirname: Optional[
|
||||||
Union[Var[Union[str, int, bool]], Union[str, int, bool]]
|
Union[Var[Union[str, int, bool]], Union[str, int, bool]]
|
||||||
@ -2038,6 +2043,7 @@ class Textarea(BaseHTML):
|
|||||||
disabled: Optional[
|
disabled: Optional[
|
||||||
Union[Var[Union[str, int, bool]], Union[str, int, bool]]
|
Union[Var[Union[str, int, bool]], Union[str, int, bool]]
|
||||||
] = None,
|
] = None,
|
||||||
|
enter_key_submit: Optional[Union[Var[bool], bool]] = None,
|
||||||
form: Optional[Union[Var[Union[str, int, bool]], Union[str, int, bool]]] = None,
|
form: Optional[Union[Var[Union[str, int, bool]], Union[str, int, bool]]] = None,
|
||||||
max_length: Optional[
|
max_length: Optional[
|
||||||
Union[Var[Union[str, int, bool]], Union[str, int, bool]]
|
Union[Var[Union[str, int, bool]], Union[str, int, bool]]
|
||||||
@ -2168,9 +2174,11 @@ class Textarea(BaseHTML):
|
|||||||
*children: The children of the component.
|
*children: The children of the component.
|
||||||
auto_complete: Whether the form control should have autocomplete enabled
|
auto_complete: Whether the form control should have autocomplete enabled
|
||||||
auto_focus: Automatically focuses the textarea when the page loads
|
auto_focus: Automatically focuses the textarea when the page loads
|
||||||
|
auto_height: Automatically fit the content height to the text (use min-height with this prop)
|
||||||
cols: Visible width of the text control, in average character widths
|
cols: Visible width of the text control, in average character widths
|
||||||
dirname: Name part of the textarea to submit in 'dir' and 'name' pair when form is submitted
|
dirname: Name part of the textarea to submit in 'dir' and 'name' pair when form is submitted
|
||||||
disabled: Disables the textarea
|
disabled: Disables the textarea
|
||||||
|
enter_key_submit: Enter key submits form (shift-enter adds new line)
|
||||||
form: Associates the textarea with a form (by id)
|
form: Associates the textarea with a form (by id)
|
||||||
max_length: Maximum number of characters allowed in the textarea
|
max_length: Maximum number of characters allowed in the textarea
|
||||||
min_length: Minimum number of characters required in the textarea
|
min_length: Minimum number of characters required in the textarea
|
||||||
|
@ -108,7 +108,9 @@ class TextArea(RadixThemesComponent, el.Textarea):
|
|||||||
rows: Optional[Union[Var[str], str]] = None,
|
rows: Optional[Union[Var[str], str]] = None,
|
||||||
value: Optional[Union[Var[str], str]] = None,
|
value: Optional[Union[Var[str], str]] = None,
|
||||||
wrap: Optional[Union[Var[str], str]] = None,
|
wrap: Optional[Union[Var[str], str]] = None,
|
||||||
|
auto_height: Optional[Union[Var[bool], bool]] = None,
|
||||||
cols: Optional[Union[Var[Union[str, int, bool]], Union[str, int, bool]]] = None,
|
cols: Optional[Union[Var[Union[str, int, bool]], Union[str, int, bool]]] = None,
|
||||||
|
enter_key_submit: Optional[Union[Var[bool], bool]] = None,
|
||||||
access_key: Optional[
|
access_key: Optional[
|
||||||
Union[Var[Union[str, int, bool]], Union[str, int, bool]]
|
Union[Var[Union[str, int, bool]], Union[str, int, bool]]
|
||||||
] = None,
|
] = None,
|
||||||
@ -232,7 +234,9 @@ class TextArea(RadixThemesComponent, el.Textarea):
|
|||||||
rows: Visible number of lines in the text control
|
rows: Visible number of lines in the text control
|
||||||
value: The controlled value of the textarea, read only unless used with on_change
|
value: The controlled value of the textarea, read only unless used with on_change
|
||||||
wrap: How the text in the textarea is to be wrapped when submitting the form
|
wrap: How the text in the textarea is to be wrapped when submitting the form
|
||||||
|
auto_height: Automatically fit the content height to the text (use min-height with this prop)
|
||||||
cols: Visible width of the text control, in average character widths
|
cols: Visible width of the text control, in average character widths
|
||||||
|
enter_key_submit: Enter key submits form (shift-enter adds new line)
|
||||||
access_key: Provides a hint for generating a keyboard shortcut for the current element.
|
access_key: Provides a hint for generating a keyboard shortcut for the current element.
|
||||||
auto_capitalize: Controls whether and how text input is automatically capitalized as it is entered/edited by the user.
|
auto_capitalize: Controls whether and how text input is automatically capitalized as it is entered/edited by the user.
|
||||||
content_editable: Indicates whether the element's content is editable.
|
content_editable: Indicates whether the element's content is editable.
|
||||||
|
Loading…
Reference in New Issue
Block a user