freya_components/
table.rs

1use dioxus::prelude::*;
2use freya_elements::{
3    self as dioxus_elements,
4    events::MouseEvent,
5};
6use freya_hooks::{
7    use_applied_theme,
8    FontTheme,
9    TableTheme,
10    TableThemeWith,
11};
12
13use crate::icons::ArrowIcon;
14
15#[allow(non_snake_case)]
16#[component]
17fn TableArrow(order_direction: OrderDirection) -> Element {
18    let TableTheme { arrow_fill, .. } = use_applied_theme!(None, table);
19    let rotate = match order_direction {
20        OrderDirection::Down => "0",
21        OrderDirection::Up => "180",
22    };
23
24    rsx!(ArrowIcon {
25        rotate: "{rotate}",
26        fill: "{arrow_fill}"
27    })
28}
29
30/// Properties for the [`TableHead`] component.
31#[derive(Props, Clone, PartialEq)]
32pub struct TableHeadProps {
33    /// The content of this table head.
34    pub children: Element,
35}
36
37/// The head of a [`Table`]. Use [`TableRow`] inside.
38#[allow(non_snake_case)]
39pub fn TableHead(TableHeadProps { children }: TableHeadProps) -> Element {
40    rsx!(
41        rect { width: "100%", {children} }
42    )
43}
44
45/// Properties for the [`TableBody`] component.
46#[derive(Props, Clone, PartialEq)]
47pub struct TableBodyProps {
48    /// The content of this table body.
49    pub children: Element,
50}
51
52/// The body of a [`Table`].
53#[allow(non_snake_case)]
54pub fn TableBody(TableBodyProps { children }: TableBodyProps) -> Element {
55    rsx!(
56        rect { width: "100%", {children} }
57    )
58}
59
60#[derive(PartialEq, Clone, Copy)]
61enum TableRowState {
62    Idle,
63    Hovering,
64}
65
66/// Properties for the [`TableRow`] component.
67#[derive(Props, Clone, PartialEq)]
68pub struct TableRowProps {
69    /// Theme override.
70    pub theme: Option<TableThemeWith>,
71    /// The content of this row.
72    children: Element,
73}
74
75/// Table row for [`Table`]. Use [`TableCell`] inside.
76///
77/// # Styling
78/// Inherits the [`TableTheme`](freya_hooks::TableTheme) theme.
79#[allow(non_snake_case)]
80pub fn TableRow(TableRowProps { theme, children }: TableRowProps) -> Element {
81    let theme = use_applied_theme!(&theme, table);
82    let mut state = use_signal(|| TableRowState::Idle);
83    let TableTheme {
84        divider_fill,
85        hover_row_background,
86        row_background,
87        ..
88    } = theme;
89    let background = if state() == TableRowState::Hovering {
90        hover_row_background
91    } else {
92        row_background
93    };
94
95    rsx!(
96        rect {
97            onmouseenter: move |_| state.set(TableRowState::Hovering),
98            onmouseleave: move |_| state.set(TableRowState::Idle),
99            direction: "horizontal",
100            width: "fill",
101            background: "{background}",
102            {children}
103        }
104        rect {
105            height: "1",
106            width: "fill",
107            background: "{divider_fill}"
108        }
109    )
110}
111
112/// Sorting direction for items in [`Table`].
113#[derive(Clone, Copy, PartialEq, Debug, Default)]
114pub enum OrderDirection {
115    /// Alternatively: descending.
116    Up,
117    /// Alternatively: ascending.
118    #[default]
119    Down,
120}
121
122/// Properties for the [`TableCell`] component.
123#[derive(Props, Clone, PartialEq)]
124pub struct TableCellProps {
125    /// The content of this cell.
126    pub children: Element,
127    /// Handler for the `onpress` event.
128    pub onpress: Option<EventHandler<MouseEvent>>,
129    /// The direction in which this TableCell's column will be ordered.
130    ///
131    /// **This is only a visual change (it changes the icon), you need to sort stuff yourself.**
132    #[props(into)]
133    pub order_direction: Option<Option<OrderDirection>>,
134    /// The padding of the cell.
135    #[props(default = "5 25".to_string(), into)]
136    pub padding: String,
137    /// The height of the cell.
138    #[props(default = "35".to_string(), into)]
139    pub height: String,
140}
141
142/// Cell for a [`Table`]. You can place anything inside.
143#[allow(non_snake_case)]
144pub fn TableCell(props: TableCellProps) -> Element {
145    let config = consume_context::<TableConfig>();
146    let width = 100.0 / config.columns as f32;
147    let TableCellProps {
148        children,
149        order_direction,
150        padding,
151        height,
152        ..
153    } = &props;
154
155    rsx!(
156        rect {
157            overflow: "clip",
158            padding: "{padding}",
159            width: "{width}%",
160            main_align: "end",
161            cross_align: "center",
162            height: "{height}",
163            direction: "horizontal",
164            onclick: move |e| {
165                if let Some(onpress) = &props.onpress {
166                    onpress.call(e);
167                }
168            },
169            if let Some(order_direction) = &order_direction {
170                rect {
171                    margin: "10",
172                    width: "10",
173                    height: "10",
174                    if let Some(order_direction) = &order_direction {
175                        TableArrow {
176                            order_direction: *order_direction
177                        }
178                    }
179                }
180            }
181            {children}
182        }
183    )
184}
185
186/// Properties for the [`Table`] component.
187#[derive(Props, Clone, PartialEq)]
188pub struct TableProps {
189    /// Width of the table. Default to `fill`.
190    #[props(default = "fill".into())]
191    pub height: String,
192    /// Theme override.
193    pub theme: Option<TableThemeWith>,
194    /// Number of columns used in the table.
195    pub columns: usize,
196    /// The content of the table.
197    pub children: Element,
198}
199
200/// Table component, composed with [`TableHead`] and [`TableBody`].
201///
202/// # Styling
203/// Inherits the [`TableTheme`](freya_hooks::TableTheme) theme.
204#[allow(non_snake_case)]
205pub fn Table(
206    TableProps {
207        height,
208        theme,
209        columns,
210        children,
211    }: TableProps,
212) -> Element {
213    let TableTheme {
214        background,
215        corner_radius,
216        divider_fill,
217        font_theme: FontTheme { color },
218        ..
219    } = use_applied_theme!(&theme, table);
220    provide_context(TableConfig { columns });
221
222    rsx!(rect {
223        overflow: "clip",
224        color: "{color}",
225        background: "{background}",
226        corner_radius: "{corner_radius}",
227        height: "{height}",
228        border: "1 outer {divider_fill}",
229        {children}
230    })
231}
232
233#[doc(hidden)]
234#[derive(Clone)]
235pub struct TableConfig {
236    columns: usize,
237}