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(
308///             TableHead::new().child(
309///                 TableRow::new()
310///                     .child(TableCell::new().child("Header 1"))
311///                     .child(TableCell::new().child("Header 2")),
312///             ),
313///         )
314///         .child(
315///             TableBody::new().child(
316///                 TableRow::new()
317///                     .child(TableCell::new().child("Data 1"))
318///                     .child(TableCell::new().child("Data 2")),
319///             ),
320///         )
321///         .child(
322///             TableBody::new().child(
323///                 TableRow::new()
324///                     .child(TableCell::new().child("Data 3"))
325///                     .child(TableCell::new().child("Data 4")),
326///             ),
327///         )
328/// }
329/// # use freya_testing::prelude::*;
330/// # launch_doc(|| {
331/// #   rect().padding(8.).center().expanded().child(
332/// #       app()
333/// #   )
334/// # }, "./images/gallery_table.png")
335/// #   .with_hook(|t| { t.move_cursor((125., 125.)); t.sync_and_update(); })
336/// #   .with_scale_factor(0.9)
337/// #   .render();
338/// ```
339///
340/// # Preview
341/// ![Table Preview][table]
342#[cfg_attr(feature = "docs",
343    doc = embed_doc_image::embed_image!("table", "images/gallery_table.png"),
344)]
345#[derive(PartialEq)]
346pub struct Table {
347    pub height: Size,
348    pub theme: Option<TableThemePartial>,
349    pub columns: usize,
350    pub children: Vec<Element>,
351    key: DiffKey,
352}
353
354impl Default for Table {
355    fn default() -> Self {
356        Self {
357            height: Size::Inner,
358            theme: None,
359            columns: 1,
360            children: vec![],
361            key: DiffKey::None,
362        }
363    }
364}
365
366impl Table {
367    pub fn new(columns: usize) -> Self {
368        Self {
369            columns,
370            ..Default::default()
371        }
372    }
373
374    pub fn height(mut self, height: impl Into<Size>) -> Self {
375        self.height = height.into();
376        self
377    }
378
379    pub fn theme(mut self, theme: TableThemePartial) -> Self {
380        self.theme = Some(theme);
381        self
382    }
383}
384
385impl ChildrenExt for Table {
386    fn get_children(&mut self) -> &mut Vec<Element> {
387        &mut self.children
388    }
389}
390
391impl KeyExt for Table {
392    fn write_key(&mut self) -> &mut DiffKey {
393        &mut self.key
394    }
395}
396
397#[derive(Clone)]
398pub struct TableConfig {
399    pub columns: usize,
400}
401
402impl TableConfig {
403    pub fn new(columns: usize) -> Self {
404        Self { columns }
405    }
406}
407
408impl Component for Table {
409    fn render(&self) -> impl IntoElement {
410        let TableTheme {
411            background,
412            corner_radius,
413            divider_fill,
414            color,
415            ..
416        } = get_theme!(&self.theme, table);
417
418        provide_context(TableConfig::new(self.columns));
419
420        rect()
421            .overflow(Overflow::Clip)
422            .color(color)
423            .background(background)
424            .corner_radius(corner_radius)
425            .height(self.height.clone())
426            .border(
427                Border::new()
428                    .alignment(BorderAlignment::Outer)
429                    .fill(divider_fill)
430                    .width(1.0),
431            )
432            .children(self.children.clone())
433    }
434
435    fn render_key(&self) -> DiffKey {
436        self.key.clone().or(self.default_key())
437    }
438}