dioxus_material/
text_field.rs1use crate::use_theme;
2use dioxus::prelude::*;
3use dioxus_spring::{use_animated, use_spring};
4use dioxus_use_mounted::use_mounted;
5use std::time::Duration;
6
7#[component]
35pub fn TextField(
36 label: String,
37 value: String,
38 onchange: EventHandler<FormEvent>,
39 background: Option<String>,
40 font_size: Option<f32>,
41 width: Option<String>,
42) -> Element {
43 let mut is_populated = use_signal(|| !value.is_empty());
44 let theme = use_theme();
45
46 let font_size = font_size.unwrap_or(theme.label_medium);
47 let spring = use_spring(
48 if is_populated() {
49 [10f32, 12f32, 16f32]
50 } else {
51 [20., font_size, 24.]
52 },
53 Duration::from_millis(50),
54 );
55
56 let mounted = use_mounted();
57 use_animated(mounted, spring, |[top, font_size, line_height]| {
58 format!(
59 r"
60 position: absolute;
61 top: {top}px;
62 left: 20px;
63 font-size: {font_size}px;
64 line-height: {line_height}px;
65 "
66 )
67 });
68
69 let background = background.as_deref().unwrap_or(&theme.background_color);
70 let width = width.as_deref().unwrap_or("200px");
71
72 rsx!(
73 div {
74 position: "relative",
75 display: "flex",
76 width,
77 background: "{background}",
78 font_family: "sans-serif",
79 border_bottom: "2px solid #999",
80 label { onmounted: move |event| mounted.onmounted(event), "{label}" }
81 input {
82 position: "relative",
83 z_index: 9,
84 r#type: "text",
85 value: value.clone(),
86 padding: "10px 20px",
87 padding_top: "30px",
88 font_size: "{font_size}px",
89 height: "34px",
90 border: "none",
91 outline: "none",
92 background: "none",
93 onfocusin: move |_| {
94 if !is_populated() {
95 is_populated.set(true)
96 }
97 },
98 onfocusout: move |_| {
99 if is_populated() && value.is_empty() {
100 is_populated.set(false)
101 }
102 },
103 oninput: move |event| onchange.call(event)
104 }
105 }
106 )
107}