Textarea
Displays a form textarea.
src/components/rahti_ui/textarea.rs
Installation
Install this component in your Rahti 0.0.58 application. Class conflicts are resolved with the existing frontend `twMerge`; no Rust class-merging dependency is needed.
Basics
`rows` sets where the box opens; `field-sizing-content` in the class list grows it past that as soon as there is enough text. `min-h-16` is the floor either way.
Initial value
The one place a textarea reads differently from an input: its value is its children, placed with `<slot />`. A textarea's content is character data, so what a user typed round-trips as text rather than becoming markup.
States
A `false` leaves the attribute off rather than writing `disabled="false"`, which HTML would read as disabled. `.invalid(true)` writes `aria-invalid`, and the `aria-invalid:` utilities in the class list do the rest.
With a label
`id` and `name` are named props for the same reason `placeholder` is: they are what a form is actually written with. Everything rarer goes through `attrs`.
Nothing here is submitted anywhere.
Attributes
`.attr(…)` writes anything; `.unset(…)` takes one away, which is the thing a value cannot express and the reason a forwarded set is an `Attrs` rather than a list of pairs. The last field has no `data-slot`.
Class override
Rust composes the component and caller class lists. When utilities conflict, use the existing frontend `twMerge` in a compiled class binding.
Controlled, page scope
A field whose value is this page's state belongs on markup this page owns. `textarea_variants` returns the same class list the component renders, so a native `<textarea>` here is a Textarea in every way that shows. Note `field-sizing-content`: the box grows as you type, with `min-h-16` as its floor.
{draft.length} of 280 characters, {draft.split("\n").length} line(s).
Validation, inline
`.render(children)` returns the element itself, with no boundary around it, so interpolating it into this block puts it in this block's scope. Handlers and bindings both work, and it is still the same component. `aria-invalid` is bound here, which is what paints the field — the accessible state and the styling are one fact.
`value` is bound too, which is what makes this field controlled: the state it writes on every keystroke is the state it reads back. A textarea nothing is bound to keeps what is typed into it and is left alone by a render — that took a runtime fix, since a textarea's value is its children and every render produces those.
{bio.length > 280 ? "Too long by " + (bio.length - 280) + " — the field is marked invalid." : "Room for " + (280 - bio.length) + " more."}
The component tag, and its boundary
The tag is the right call for a field this page does not drive — a name, a placeholder, a `rows`, a `required`. What a plain form posts, the tag renders.
Through the tag
The same as an Input. A component's root attributes are evaluated where the tag was written, so `value` and `oninput` read and write this page's state, as a Button's onclick prop drives a page's counter.
The two sections above are the other spellings, for a field assembled in Rust or an element this component does not render.