freya_components/
table.rs

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