dioxus_sortable/
lib.rs

1#![warn(missing_docs)]
2//!
3//! # Sortable components for Dioxus
4//!
5//! Create sortable tables (and other components) of any type for [Dioxus](https://dioxuslabs.com/).
6//!
7//! The focus is on tables but this library can be used to create any type of sortable component. Your tables can be customised however you wish. Sorting state is kept separately from the data.
8//!
9//! Throughout this documentation, you'll see the type `T` used to refer to the data type that you wish to sort. You'll also see `F` which is expected to be an enum referring to each sortable field of `T`. Your `F` enum should implement [`PartialOrdBy`] to sort and [`Sortable`] to describe how it may be sorted.
10//!
11//! We use [`PartialOrd`] to allow sorting of types with NULL semantics. This is useful where we have `f64::NAN` or an "unknown" field. It allows us to handle more general cases. We try to keep ordering semantics the same as SQL's ORDER BY clause.
12//!
13//! ## Usage
14//!
15//! 1. Create a `struct T` that you wish to sort. The table row.
16//! 2. Create an `enum F` that describes each sortable field in `T`.
17//! 3. Implement [`PartialOrdBy`] for `F`. This is used to sort `T` by `F`.
18//! 4. Implement [`Sortable`] for `F`. This is used to describe how `F` may be sorted.
19//! 5. Call [`use_sorter()`] in your component and get a [`UseSorter`].
20//! 6. Call [`UseSorter::sort`] to sort data. This may be called conditionally e.g., when waiting for data to arrive.
21//! 7. Create a table using [`Th`] or write your own with [`ThStatus`] and [`UseSorter::toggle_field`].
22//!
23//! ## Examples
24//!
25//! See a full example of [British prime ministers](https://feral-dot-io.github.io/dioxus-sortable/examples/prime-ministers/) ([and the code](https://github.com/feral-dot-io/dioxus-sortable/blob/master/examples/prime_ministers.rs)). You can modify and run it locally with `dioxus serve --example prime_ministers`
26//!
27//! A minimal example giving a tour of how to use the library is below.
28//!
29//! ```rust
30//! use dioxus::prelude::*;
31//! use dioxus_sortable::{use_sorter, PartialOrdBy, SortBy, Sortable, Th};
32//!
33//! /// Our table row. Type `T`.
34//! #[derive(Clone, Debug, PartialEq)]
35//! struct Person {
36//!     name: String,
37//!     age: u8,
38//! }
39//!
40//! /// Our table columns. Type `F`. One for each field in Person.
41//! #[derive(Copy, Clone, Debug, Default, PartialEq)]
42//! enum PersonField {
43//!     Name,
44//!     /// Use default for the initial sort.
45//!     #[default]
46//!     Age,
47//! }
48//!
49//! /// Specify how we sort our `Person` using `PersonField`.
50//! impl PartialOrdBy<Person> for PersonField {
51//!     fn partial_cmp_by(&self, a: &Person, b: &Person) -> Option<std::cmp::Ordering> {
52//!         // Note how it's just a passthru to `PartialOrd` for each field.
53//!         match self {
54//!             PersonField::Name => a.name.partial_cmp(&b.name),
55//!             PersonField::Age => a.age.partial_cmp(&b.age),
56//!         }
57//!     }
58//! }
59//!
60//! /// Specify sorting options available on a column.
61//! impl Sortable for PersonField {
62//!     fn sort_by(&self) -> Option<SortBy> {
63//!         // We can choose column specifics but this is good for the minimum.
64//!         SortBy::increasing_or_decreasing()
65//!     }
66//! }
67//!
68//! #[inline_props]
69//! fn OurMinimalExampleTable(cx: Scope) -> Element {
70//!     // Set up Dioxus state hooks. *Must* be called every time in the same order
71//!     let sorter = use_sorter::<PersonField>(cx);
72//!     // Obtain our data. Either passed via props or pulled from a server
73//!     let mut data = load_data();
74//!     // Sort our data. This is optional but needed to apply the sort
75//!     sorter.sort(data.as_mut_slice());
76//!
77//!     // Render our table like normal.
78//!     cx.render(rsx! {
79//!         table {
80//!             thead {
81//!                 tr {
82//!                     // Note that we use `Th` instead of `th`.
83//!                     // We could customise `th` by using `ThStatus` instead.
84//!                     // Or use `UseSorter::toggle_field` elsewhere to lift
85//!                     // the sorter out of the table entirely.
86//!                     Th { sorter: sorter, field: PersonField::Name, "Name" }
87//!                     Th { sorter: sorter, field: PersonField::Age, "Age" }
88//!                 }
89//!             }
90//!             tbody {
91//!                 // Iterate on our sorted data.
92//!                 // If we didn't want sortable tables, this could easily be a
93//!                 // `ul { li { ... } }` instead.
94//!                 for person in data.iter() {
95//!                     tr {
96//!                         td { "{person.name}" }
97//!                         td { "{person.age}" }
98//!                     }
99//!                 }
100//!             }
101//!         }
102//!    })
103//! }
104//!
105//! # fn load_data() -> Vec<Person> {
106//! #     vec![
107//! #         Person { name: "John".to_string(), age: 32 },
108//! #         Person { name: "Jane".to_string(), age: 28 },
109//! #         Person { name: "Bob".to_string(), age: 42 },
110//! #     ]
111//! # }
112//! ```
113//!
114
115mod rsx;
116pub use rsx::*;
117mod use_sorter;
118pub use use_sorter::*;