1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109
use crate::use_theme;
use dioxus::prelude::*;
use dioxus_spring::{use_animated, use_spring};
use dioxus_use_mounted::use_mounted;
use std::time::Duration;
/// Text field component.
///
/// Text fields let users enter text into a UI.
///
/// [material.io](https://m3.material.io/components/text-fields)
///
/// ## Panics
/// This component requires access to a [`Theme`](crate::Theme).
///
/// ## Examples
/// ```rust
/// use dioxus::prelude::*;
/// use dioxus_material::{TextField, Theme};
///
/// fn app(cx: Scope) -> Element {
/// let value = use_state(cx, || String::from("Filled"));
/// render!(
/// Theme {
/// TextField {
/// label: "Text field",
/// value: "{value}",
/// onchange: move |event: FormEvent| value.set(event.value.clone())
/// }
/// }
/// )
/// }
/// ```
#[component]
pub fn TextField<'a>(
cx: Scope<'a>,
label: &'a str,
value: &'a str,
onchange: EventHandler<'a, FormEvent>,
background: Option<&'a str>,
font_size: Option<f32>,
width: Option<&'a str>,
) -> Element<'a> {
let is_populated = use_state(cx, || !value.is_empty());
let theme = use_theme(cx);
let font_size = font_size.unwrap_or(theme.label_medium);
let spring = use_spring(
cx,
if **is_populated {
[10f32, 12f32, 16f32]
} else {
[20., font_size, 24.]
},
Duration::from_millis(50),
);
let mounted = use_mounted(cx);
use_animated(cx, mounted, spring, |[top, font_size, line_height]| {
format!(
r"
position: absolute;
top: {top}px;
left: 20px;
font-size: {font_size}px;
line-height: {line_height}px;
"
)
});
let background = background.unwrap_or(&theme.background_color);
let width = width.unwrap_or("200px");
render!(
div {
position: "relative",
display: "flex",
width: width,
background: "{background}",
font_family: "sans-serif",
border_bottom: "2px solid #999",
label { onmounted: move |event| mounted.onmounted(event), "{label}" }
input {
position: "relative",
z_index: 9,
r#type: "text",
value: *value,
padding: "10px 20px",
padding_top: "30px",
font_size: "{font_size}px",
height: "34px",
border: "none",
outline: "none",
background: "none",
onfocusin: move |_| {
if !is_populated {
is_populated.set(true)
}
},
onfocusout: move |_| {
if **is_populated && value.is_empty() {
is_populated.set(false)
}
},
oninput: move |event| onchange.call(event)
}
}
)
}