dioxus_sortable/
rsx.rs

1#![allow(non_snake_case)]
2use crate::{Direction, SortBy, Sortable, UseSorter};
3use dioxus::prelude::*;
4
5/// See [`Th`].
6#[derive(Props)]
7pub struct ThProps<'a, F: 'static> {
8    sorter: UseSorter<'a, F>,
9    field: F,
10    children: Element<'a>,
11}
12
13/// Convenience helper. Builds a `<th>` element with a click handler that calls [`UseSorter::toggle_field`]. Renders the current state using [`ThStatus`].
14pub fn Th<'a, F: Copy + Sortable>(cx: Scope<'a, ThProps<'a, F>>) -> Element<'a> {
15    let sorter = cx.props.sorter;
16    let field = cx.props.field;
17    cx.render(rsx! {
18        th {
19            onclick: move |_| sorter.toggle_field(field),
20            &cx.props.children
21            ThStatus {
22                sorter: sorter,
23                field: field,
24            }
25        }
26    })
27}
28
29/// See [`ThStatus`].
30#[derive(PartialEq, Props)]
31pub struct ThStatusProps<'a, F: 'static> {
32    sorter: UseSorter<'a, F>,
33    field: F,
34}
35
36/// Convenience helper. Renders the [`Sortable`] value for a given [`UseSorter`] and field.
37///  - If the field is unsortable then render an empty string.
38///  - If the field is sortable in one direction then render an arrow pointing in that direction.
39///  - If the field is sortable in both directions then render an arrow pointing in the active direction, or a double-headed arrow if the field is inactive.
40///
41/// Active fields will be shown in bold (i.e., the current field being sorted by). Inactive fields will be greyed out.
42pub fn ThStatus<'a, F: Copy + Sortable>(cx: Scope<'a, ThStatusProps<'a, F>>) -> Element<'a> {
43    let sorter = &cx.props.sorter;
44    let field = cx.props.field;
45    let (active_field, active_dir) = sorter.get_state();
46    let active = *active_field == field;
47
48    cx.render(match field.sort_by() {
49        None => rsx!(""),
50        Some(sort_by) => {
51            use Direction::*;
52            use SortBy::*;
53            match sort_by {
54                Fixed(Ascending) => rsx!(ThSpan { active: active, "↓" }),
55                Fixed(Descending) => rsx!(ThSpan { active: active, "↑" }),
56
57                Reversible(_) => rsx!(
58                ThSpan {
59                    active: active,
60                    match (active, active_dir) {
61                        (true, Direction::Ascending) => "↓",
62                        (true, Direction::Descending) => "↑",
63                        (false, _) => "↕",
64                    }
65                }),
66            }
67        }
68    })
69}
70
71/// See [`ThSpan`].
72#[derive(Props)]
73struct ThSpan<'a> {
74    active: bool,
75    children: Element<'a>,
76}
77
78/// Convenience helper. Renders an active or inactive gielement.
79fn ThSpan<'a>(cx: Scope<'a, ThSpan<'a>>) -> Element<'a> {
80    let colour = if cx.props.active { "#555" } else { "#ccc" };
81    let nbsp = "&nbsp;";
82    cx.render(rsx! {
83        span {
84            style: "color: {colour};",
85            span { dangerous_inner_html: "{nbsp}", }
86            &cx.props.children
87        }
88    })
89}