1use freya_core::prelude::*;
2use torin::{
3 content::Content,
4 gaps::Gaps,
5 prelude::Alignment,
6 size::Size,
7};
8
9use crate::{
10 define_theme,
11 get_theme,
12 icons::arrow::ArrowIcon,
13};
14
15define_theme! {
16 %[component]
17 pub Table {
18 %[fields]
19 background: Color,
20 arrow_fill: Color,
21 hover_row_background: Color,
22 row_background: Color,
23 divider_fill: Color,
24 corner_radius: CornerRadius,
25 color: Color,
26 }
27}
28
29#[derive(Clone, Copy, PartialEq, Default)]
30pub enum OrderDirection {
31 Up,
32 #[default]
33 Down,
34}
35
36#[derive(PartialEq)]
37pub struct TableArrow {
38 pub order_direction: OrderDirection,
39 key: DiffKey,
40}
41
42impl TableArrow {
43 pub fn new(order_direction: OrderDirection) -> Self {
44 Self {
45 order_direction,
46 key: DiffKey::None,
47 }
48 }
49}
50
51impl KeyExt for TableArrow {
52 fn write_key(&mut self) -> &mut DiffKey {
53 &mut self.key
54 }
55}
56
57impl Component for TableArrow {
58 fn render(&self) -> impl IntoElement {
59 let TableTheme { arrow_fill, .. } =
60 get_theme!(None::<TableThemePartial>, TableThemePreference, "table");
61 let rotate = match self.order_direction {
62 OrderDirection::Down => 0.,
63 OrderDirection::Up => 180.,
64 };
65 ArrowIcon::new().rotate(rotate).fill(arrow_fill)
66 }
67
68 fn render_key(&self) -> DiffKey {
69 self.key.clone().or(self.default_key())
70 }
71}
72
73#[derive(PartialEq, Default)]
75pub struct TableHead {
76 pub children: Vec<Element>,
77 key: DiffKey,
78}
79
80impl TableHead {
81 pub fn new() -> Self {
82 Self::default()
83 }
84}
85
86impl ChildrenExt for TableHead {
87 fn get_children(&mut self) -> &mut Vec<Element> {
88 &mut self.children
89 }
90}
91
92impl KeyExt for TableHead {
93 fn write_key(&mut self) -> &mut DiffKey {
94 &mut self.key
95 }
96}
97
98impl Component for TableHead {
99 fn render(&self) -> impl IntoElement {
100 rect().width(Size::fill()).children(self.children.clone())
101 }
102
103 fn render_key(&self) -> DiffKey {
104 self.key.clone().or(self.default_key())
105 }
106}
107
108#[derive(PartialEq, Default)]
109pub struct TableBody {
110 pub children: Vec<Element>,
111 key: DiffKey,
112}
113
114impl TableBody {
115 pub fn new() -> Self {
116 Self::default()
117 }
118}
119impl ChildrenExt for TableBody {
120 fn get_children(&mut self) -> &mut Vec<Element> {
121 &mut self.children
122 }
123}
124
125impl KeyExt for TableBody {
126 fn write_key(&mut self) -> &mut DiffKey {
127 &mut self.key
128 }
129}
130
131impl Component for TableBody {
132 fn render(&self) -> impl IntoElement {
133 rect().width(Size::fill()).children(self.children.clone())
134 }
135
136 fn render_key(&self) -> DiffKey {
137 self.key.clone().or(self.default_key())
138 }
139}
140
141#[derive(PartialEq, Clone, Copy)]
142enum TableRowState {
143 Idle,
144 Hovering,
145}
146
147#[derive(PartialEq, Default)]
148pub struct TableRow {
149 pub theme: Option<TableThemePartial>,
150 pub children: Vec<Element>,
151 key: DiffKey,
152}
153
154impl TableRow {
155 pub fn new() -> Self {
156 Self::default()
157 }
158}
159
160impl ChildrenExt for TableRow {
161 fn get_children(&mut self) -> &mut Vec<Element> {
162 &mut self.children
163 }
164}
165
166impl KeyExt for TableRow {
167 fn write_key(&mut self) -> &mut DiffKey {
168 &mut self.key
169 }
170}
171
172impl Component for TableRow {
173 fn render(&self) -> impl IntoElement {
174 let theme = get_theme!(&self.theme, TableThemePreference, "table");
175 let config = use_try_consume::<TableConfig>().unwrap_or_default();
176 let mut state = use_state(|| TableRowState::Idle);
177 let TableTheme {
178 divider_fill,
179 hover_row_background,
180 row_background,
181 ..
182 } = theme;
183 let background = if state() == TableRowState::Hovering {
184 hover_row_background
185 } else {
186 row_background
187 };
188
189 rect()
190 .on_pointer_enter(move |_| state.set(TableRowState::Hovering))
191 .on_pointer_leave(move |_| state.set(TableRowState::Idle))
192 .background(background)
193 .child(
194 rect()
195 .width(Size::fill())
196 .horizontal()
197 .content(Content::Flex)
198 .children(self.children.iter().enumerate().map(|(index, child)| {
199 let width = config
200 .column_widths
201 .as_ref()
202 .and_then(|widths| widths.get(index).cloned())
203 .unwrap_or_else(|| Size::flex(1.));
204
205 rect().width(width).child(child.clone()).into()
206 })),
207 )
208 .child(
209 rect()
210 .height(Size::px(1.))
211 .width(Size::fill())
212 .background(divider_fill),
213 )
214 }
215
216 fn render_key(&self) -> DiffKey {
217 self.key.clone().or(self.default_key())
218 }
219}
220
221#[derive(PartialEq)]
222pub struct TableCell {
223 pub children: Vec<Element>,
224 pub on_press: Option<EventHandler<Event<PressEventData>>>,
226 pub order_direction: Option<OrderDirection>,
228 pub padding: Gaps,
230 pub height: Size,
232 key: DiffKey,
233}
234
235impl ChildrenExt for TableCell {
236 fn get_children(&mut self) -> &mut Vec<Element> {
237 &mut self.children
238 }
239}
240
241impl Default for TableCell {
242 fn default() -> Self {
243 Self {
244 children: vec![],
245 on_press: None,
246 order_direction: None,
247 padding: Gaps::new_all(5.0),
248 height: Size::px(35.0),
249 key: DiffKey::None,
250 }
251 }
252}
253
254impl TableCell {
255 pub fn new() -> Self {
256 Self::default()
257 }
258
259 pub fn padding(mut self, padding: Gaps) -> Self {
260 self.padding = padding;
261 self
262 }
263
264 pub fn height(mut self, height: impl Into<Size>) -> Self {
265 self.height = height.into();
266 self
267 }
268
269 pub fn on_press(mut self, handler: impl Into<EventHandler<Event<PressEventData>>>) -> Self {
270 self.on_press = Some(handler.into());
271 self
272 }
273
274 pub fn order_direction(mut self, dir: Option<OrderDirection>) -> Self {
275 self.order_direction = dir;
276 self
277 }
278}
279
280impl KeyExt for TableCell {
281 fn write_key(&mut self) -> &mut DiffKey {
282 &mut self.key
283 }
284}
285
286impl Component for TableCell {
287 fn render(&self) -> impl IntoElement {
288 let mut container = rect()
289 .overflow(Overflow::Clip)
290 .padding(self.padding)
291 .width(Size::fill())
292 .main_align(Alignment::End)
293 .cross_align(Alignment::Center)
294 .height(self.height.clone())
295 .horizontal();
296
297 if let Some(on_press) = &self.on_press {
298 let handler = on_press.clone();
299 container = container.on_press(move |e| handler.call(e));
300 }
301
302 if let Some(order_direction) = self.order_direction {
303 container = container.child(
304 rect()
305 .margin(Gaps::new_all(10.0))
306 .width(Size::px(10.0))
307 .height(Size::px(10.0))
308 .child(TableArrow::new(order_direction)),
309 );
310 }
311
312 container.children(self.children.clone())
313 }
314
315 fn render_key(&self) -> DiffKey {
316 self.key.clone().or(self.default_key())
317 }
318}
319
320#[cfg_attr(feature = "docs",
364 doc = embed_doc_image::embed_image!("table", "images/gallery_table.png"),
365)]
366#[derive(PartialEq)]
367pub struct Table {
368 pub height: Size,
369 pub theme: Option<TableThemePartial>,
370 pub column_widths: Option<Vec<Size>>,
371 pub children: Vec<Element>,
372 key: DiffKey,
373}
374
375impl Default for Table {
376 fn default() -> Self {
377 Self {
378 height: Size::Inner,
379 theme: None,
380 column_widths: None,
381 children: vec![],
382 key: DiffKey::None,
383 }
384 }
385}
386
387impl Table {
388 pub fn new() -> Self {
389 Self {
390 ..Default::default()
391 }
392 }
393
394 pub fn height(mut self, height: impl Into<Size>) -> Self {
395 self.height = height.into();
396 self
397 }
398
399 pub fn theme(mut self, theme: TableThemePartial) -> Self {
400 self.theme = Some(theme);
401 self
402 }
403
404 pub fn column_widths(mut self, widths: impl Into<Vec<Size>>) -> Self {
408 self.column_widths = Some(widths.into());
409 self
410 }
411}
412
413impl ChildrenExt for Table {
414 fn get_children(&mut self) -> &mut Vec<Element> {
415 &mut self.children
416 }
417}
418
419impl KeyExt for Table {
420 fn write_key(&mut self) -> &mut DiffKey {
421 &mut self.key
422 }
423}
424
425#[derive(Clone, Default)]
426pub struct TableConfig {
427 pub column_widths: Option<Vec<Size>>,
428}
429
430impl TableConfig {
431 pub fn new() -> Self {
432 Self::default()
433 }
434
435 pub fn with_column_widths(column_widths: Vec<Size>) -> Self {
436 Self {
437 column_widths: Some(column_widths),
438 }
439 }
440}
441
442impl Component for Table {
443 fn render(&self) -> impl IntoElement {
444 let TableTheme {
445 background,
446 corner_radius,
447 divider_fill,
448 color,
449 ..
450 } = get_theme!(&self.theme, TableThemePreference, "table");
451
452 let config = match &self.column_widths {
453 Some(widths) => TableConfig::with_column_widths(widths.clone()),
454 None => TableConfig::default(),
455 };
456 provide_context(config);
457
458 rect()
459 .overflow(Overflow::Clip)
460 .color(color)
461 .background(background)
462 .corner_radius(corner_radius)
463 .height(self.height.clone())
464 .border(
465 Border::new()
466 .alignment(BorderAlignment::Outer)
467 .fill(divider_fill)
468 .width(1.0),
469 )
470 .children(self.children.clone())
471 }
472
473 fn render_key(&self) -> DiffKey {
474 self.key.clone().or(self.default_key())
475 }
476}