cranpose_liquid/widgets/
search_field.rs1use crate::material::Glass;
4use crate::material::LiquidModifierExt;
5use crate::theme::{liquid_colors, liquid_typography};
6use cranpose_foundation::text::TextFieldState;
7use cranpose_macros::composable;
8use cranpose_ui::text::{SpanStyle, TextStyle};
9use cranpose_ui::widgets::{BasicTextField, Box, BoxSpec, Row, RowSpec, Text};
10use cranpose_ui::Modifier;
11use cranpose_ui_layout::{Alignment, VerticalAlignment};
12
13#[derive(Clone, Debug, PartialEq)]
15pub struct LiquidSearchFieldSpec {
16 pub placeholder: String,
17 pub on_glass: bool,
19}
20
21impl Default for LiquidSearchFieldSpec {
22 fn default() -> Self {
23 Self {
24 placeholder: "Search".to_string(),
25 on_glass: true,
26 }
27 }
28}
29
30#[composable]
32#[allow(non_snake_case)]
33pub fn LiquidSearchField(modifier: Modifier, state: TextFieldState, spec: LiquidSearchFieldSpec) {
34 let colors = liquid_colors();
35 let typography = liquid_typography();
36
37 let base = if spec.on_glass {
38 Modifier::empty().glass_effect(Glass::regular())
39 } else {
40 let fill = colors.fill;
41 Modifier::empty()
42 .rounded_corners(999.0)
43 .draw_behind(move |scope| {
44 scope.draw_round_rect(
45 cranpose_ui_graphics::Brush::solid(fill),
46 cranpose_ui_graphics::CornerRadii::uniform(999.0),
47 );
48 })
49 };
50
51 let placeholder = spec.placeholder.clone();
52 Box(
53 base.then(modifier).padding_symmetric(14.0, 9.0),
54 BoxSpec::default().content_alignment(Alignment::new(
55 cranpose_ui_layout::HorizontalAlignment::Start,
56 cranpose_ui_layout::VerticalAlignment::CenterVertically,
57 )),
58 move || {
59 let placeholder = placeholder.clone();
60 let typography = typography.clone();
61 let state = state.clone();
62 Row(
63 Modifier::empty().fill_max_width(),
64 RowSpec::default().vertical_alignment(VerticalAlignment::CenterVertically),
65 move || {
66 crate::icons::Icon(crate::icons::SEARCH, 18.0, colors.secondary_label);
67 Box(Modifier::empty().width(8.0), BoxSpec::default(), || {});
68
69 let is_empty = state.text().is_empty();
70 let field_style = TextStyle {
71 span_style: SpanStyle {
72 color: Some(colors.label),
73 ..typography.body.span_style.clone()
74 },
75 ..typography.body.clone()
76 };
77 let placeholder = placeholder.clone();
78 let placeholder_typography = typography.clone();
79 let state = state.clone();
80 Box(
85 Modifier::empty().weight(1.0),
86 BoxSpec::default(),
87 move || {
88 if is_empty {
89 let placeholder_style = TextStyle {
90 span_style: SpanStyle {
91 color: Some(colors.tertiary_label),
92 ..placeholder_typography.body.span_style.clone()
93 },
94 ..placeholder_typography.body.clone()
95 };
96 Text(placeholder.clone(), Modifier::empty(), placeholder_style);
97 }
98 BasicTextField(
99 state.clone(),
100 Modifier::empty().fill_max_width(),
101 field_style.clone(),
102 );
103 },
104 );
105 },
106 );
107 },
108 );
109}