freya_components/
table.rs

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