Expanding text input fields?

As far as I can tell, Dash does not provide a text input element that grows vertically as the text wraps. The only way I can think to pull this off is to use a callback which calculates how many times the text has wrapped in the input element as text is entered, then update the TextArea height accordingly. Is this feasible? Or is there a built-in means of pulling this off? Thanks for your help!

Welcome back to the forum @adiadidas15 :slightly_smiling_face:

Sigh, I wish the browser had built-in ways to do this better. Turns out it’s actually one of those dark corners of HTML without a native solution, so folks have been working on different workarounds six ways to sunday. Here’s a post explaining some of them, including a CSS solution with a JS one-liner at the very end that some commenters proclaim as “genius” :upside_down_face: . Getting that JS snippet to run after your text area has rendered might take a little bit of finessing though… will probably need to set a setInterval that continuously checks and waits for the element to appear…

1 Like

Here’s an example based on the clever solution mentioned by @chriddyp :smiley:

autoresizing-inputs

And here’s the code

app.py

from dash import Dash, html, dcc, clientside_callback, Output, Input


app = Dash(__name__)
app.layout = html.Div(
    [
        html.Label(
            [
                html.Span("Name: "),
                dcc.Input(id="name_input", size="4", placeholder="John")
            ],
            className="input-sizer",
        ),
        html.Label(
            [
                html.Span("Text: "),
                dcc.Textarea(rows=1, id="text_input", placeholder="Hello World!")
            ],
            className="input-sizer stacked",
        ),
    ],
    style={"display": "flex", "flexDirection": "column", "alignItems": "start"}
)


for element_id in ["name_input", "text_input"]:
    clientside_callback(
        """function(id, value) {
            document.getElementById(id).parentNode.dataset.value = value
            return window.dash_clientside.no_update
        }""",
        Output(element_id, "className"),
        [Input(element_id, "id"), Input(element_id, "value")]
    )


if __name__ == "__main__":
    app.run_server(debug=True)

assets/style.css

*,
*::before,
*::after {
  box-sizing: border-box;
}

.input-sizer {
  display: inline-grid;
  vertical-align: top;
  align-items: center;
  position: relative;
  border: solid 1px;
  padding: 0.25em 0.5em;
  margin: 5px;
  box-shadow: 4px 4px 0px #000;
}

.input-sizer.stacked {
  padding: 0.5em;
  align-items: stretch;
}

.input-sizer.stacked::after,
.input-sizer.stacked input,
.input-sizer.stacked textarea {
  grid-area: 2 / 1;
}

.input-sizer::after,
.input-sizer input,
.input-sizer textarea {
  width: auto;
  min-width: 1em;
  grid-area: 1 / 2;
  font: inherit;
  padding: 0.25em;
  margin: 0;
  resize: none;
  background: none;
  appearance: none;
  border: none;
}

.input-sizer textarea {
  overflow: hidden;
}

.input-sizer::after {
  content: attr(data-value) " ";
  visibility: hidden;
  white-space: pre-wrap;
}

.input-sizer:focus-within {
  outline: solid 1px blue;
  box-shadow: 4px 4px 0px blue;
}

.input-sizer:focus-within > span {
  color: blue;
}

.input-sizer:focus-within textarea:focus,
.input-sizer:focus-within input:focus {
  outline: none;
}

.input-sizer span {
  padding: 0.25em;
}

.input-sizer > span {
  text-transform: uppercase;
  font-size: 0.8em;
  font-weight: bold;
  text-shadow: 2px 2px 0 rgba(0, 0, 0, 0.15);
}

4 Likes

Nice @RenaudLN !!

If you want, you can give Dash Mantine Components a try.

Use the Textarea component with autosize prop set to True.

dmc.Textarea(
    label="Autosize with no rows limit",
    placeholder="Autosize with no rows limit",
    style={"width": 500},
    autosize=True,
    minRows=2
 )
5 Likes

Thanks @chriddyp, @RenaudLN & @snehilvj! The Dash Mantine Textarea component was the silver bullet I was hoping for.

My use case was for a tool to help people memorize arbitrary text, which has nothing to do with data visualization. But since my only experience with modern web development is with Dash, I decided to see if I could pull it off with Dash. I’m pretty happy with the result.
https://reciteit.herokuapp.com/