repose_material/material3/
mod.rs1#![allow(non_snake_case)]
2
3use std::rc::Rc;
4
5use repose_core::*;
6use repose_ui::{
7 Box, Column, Row, Spacer, Stack, Surface, Text, TextStyle, ViewExt, anim::animate_f32,
8};
9
10pub fn AlertDialog(
11 visible: bool,
12 on_dismiss: impl Fn() + 'static,
13 title: View,
14 text: View,
15 confirm_button: View,
16 dismiss_button: Option<View>,
17) -> View {
18 if !visible {
19 return Box(Modifier::new());
20 }
21
22 Stack(Modifier::new().fill_max_size()).child((
23 Box(Modifier::new()
25 .fill_max_size()
26 .background(Color::from_hex("#000000AA"))
27 .clickable()
28 .on_pointer_down(move |_| on_dismiss())),
29 Surface(
31 Modifier::new()
32 .size(280.0, 200.0)
33 .background(theme().surface)
34 .clip_rounded(28.0)
35 .padding(24.0),
36 Column(Modifier::new()).child((
37 title,
38 Box(Modifier::new().size(1.0, 16.0)),
39 text,
40 Spacer(),
41 Row(Modifier::new()).child((
42 dismiss_button.unwrap_or(Box(Modifier::new())),
43 Spacer(),
44 confirm_button,
45 )),
46 )),
47 ),
48 ))
49}
50
51pub fn BottomSheet(
52 visible: bool,
53 on_dismiss: impl Fn() + 'static,
54 modifier: Modifier,
55 content: View,
56) -> View {
57 let offset = animate_f32(
58 "sheet_offset",
59 if visible { 0.0 } else { 800.0 },
60 AnimationSpec::spring_gentle(),
61 );
62
63 Stack(Modifier::new().fill_max_size()).child((
64 if visible {
66 Box(Modifier::new()
67 .fill_max_size()
68 .background(Color::from_hex("#00000055"))
69 .on_pointer_down(move |_| on_dismiss()))
70 } else {
71 Box(Modifier::new())
72 },
73 Box(modifier
75 .absolute()
76 .offset(None, Some(offset), Some(0.0), Some(0.0)))
77 .child(content),
78 ))
79}
80
81pub fn NavigationBar(selected_index: usize, items: Vec<NavItem>) -> View {
82 Row(Modifier::new()
83 .fill_max_size()
84 .background(theme().surface)
85 .padding(8.0))
86 .child(
87 items
88 .into_iter()
89 .enumerate()
90 .map(|(i, item)| NavigationBarItem(item, i == selected_index))
91 .collect::<Vec<_>>(),
92 )
93}
94
95pub struct NavItem {
96 pub icon: View,
97 pub label: String,
98 pub on_click: Rc<dyn Fn()>,
99}
100
101fn NavigationBarItem(item: NavItem, selected: bool) -> View {
102 let color = if selected {
103 theme().primary
104 } else {
105 theme().on_surface
106 };
107
108 Column(
109 Modifier::new()
110 .flex_grow(1.0)
111 .clickable()
112 .on_pointer_down(move |_| (item.on_click)()),
113 )
114 .child((
115 item.icon, Text(item.label).color(color),
117 ))
118}
119
120pub fn Card(modifier: Modifier, elevated: bool, content: View) -> View {
121 Surface(
122 modifier
123 .background(theme().surface)
124 .border(1.0, Color::from_hex("#22222222"), 12.0)
125 .clip_rounded(12.0)
126 .padding(16.0),
127 content,
128 )
129}
130
131pub fn OutlinedCard(modifier: Modifier, content: View) -> View {
132 Surface(
133 modifier
134 .border(1.0, Color::from_hex("#444444"), 12.0)
135 .clip_rounded(12.0)
136 .padding(16.0),
137 content,
138 )
139}
140
141pub fn FilterChip(
142 selected: bool,
143 on_click: impl Fn() + 'static,
144 label: View,
145 leading_icon: Option<View>,
146) -> View {
147 let bg = if selected {
148 theme().primary
149 } else {
150 theme().surface
151 };
152 let fg = if selected {
153 theme().on_primary
154 } else {
155 theme().on_surface
156 };
157
158 Surface(
159 Modifier::new()
160 .background(bg)
161 .border(1.0, Color::from_hex("#444444"), 8.0)
162 .clip_rounded(8.0)
163 .padding(12.0)
164 .clickable()
165 .on_pointer_down(move |_| on_click()),
166 Row(Modifier::new()).child((leading_icon.unwrap_or(Box(Modifier::new())), label)),
167 )
168}
169
170pub fn Scaffold(
171 top_bar: Option<View>,
172 bottom_bar: Option<View>,
173 floating_action_button: Option<View>,
174 content: impl Fn(PaddingValues) -> View,
175) -> View {
176 Stack(Modifier::new().fill_max_size()).child((
177 Box(Modifier::new()
179 .fill_max_size()
180 .padding_values(PaddingValues {
181 top: if top_bar.is_some() { 64.0 } else { 0.0 },
182 bottom: if bottom_bar.is_some() { 80.0 } else { 0.0 },
183 ..Default::default()
184 }))
185 .child(content(PaddingValues::default())),
186 if let Some(bar) = top_bar {
188 Box(Modifier::new()
189 .absolute()
190 .offset(Some(0.0), Some(0.0), Some(0.0), None))
191 .child(bar)
192 } else {
193 Box(Modifier::new())
194 },
195 if let Some(bar) = bottom_bar {
197 Box(Modifier::new()
198 .absolute()
199 .offset(Some(0.0), None, Some(0.0), Some(0.0)))
200 .child(bar)
201 } else {
202 Box(Modifier::new())
203 },
204 if let Some(fab) = floating_action_button {
206 Box(Modifier::new()
207 .absolute()
208 .offset(None, None, Some(16.0), Some(16.0)))
209 .child(fab)
210 } else {
211 Box(Modifier::new())
212 },
213 ))
214}