# Dioxus Table Crates.io version GitHub Sponsors Make data-driven table rendering easy with Dioxus ([Live Example](https://dioxus-table.netlify.app/)) ## Installation Add the following to your Cargo.toml ```toml [dependencies] dioxus-table = "0.1.1" ``` ## Quickstart Attach the derive macro `TableData` to a struct that represents a row of a table. ```rust // in mod hotel: #[derive(PartialEq, TableData)] pub struct Hotel { #[table(class = "text-end")] // right align numbers pub id: i32, #[table(title = "Hotel Name")] // custom title pub name: String, pub city: String, #[table(skip)] // don't show this column pub internal: u32, } ``` This generates a Dioxus component called `Table` in the module `hotel` ready to be used in your app. ```rust // in your app: use hotel::{Hotel, Table as HotelTable}; fn App(cx: Scope) -> Element { // get some hotels let hotels = vec![ Hotel { id: 1, name: "Hotel 1".to_owned(), city: "City 1".to_owned(), internal: 42 }, Hotel { id: 2, name: "Hotel 2".to_owned(), city: "City 2".to_owned(), internal: 42 }, ]; cx.render(rsx! { h1 { "Hotel Table" } HotelTable { class: "table", items: &hotels, } }) } ``` And that's it! Easy, right? You can look at the examples in the `examples` directory to get a more complete overview of what dioxus-table can do. ## Event Handlers The generated table component provides two events: Clicking on a row or a head cell. Let's add some event handlers to our previous example. ```rust cx.render(rsx! { h1 { "Hotel Table" } HotelTable { class: "table", items: &hotels, onrowclick: move |evt: TableRowEvent<_, _>| { web_sys::console::log_1(&format!("Row {}", evt.row_index).into()); }, onheadclick: move |evt: TableHeadEvent<_>| { web_sys::console::log_1(&format!("Head {} '{}'", evt.column_index, evt.field).into()); }, } }) ``` When you click on a head cell or on a data row you'll see some information logged to the console. ## Customization You can customize most aspects of the table rendering. Here is an overview of all available options. ### Per-column/field options | Option | Description | |:-----------|:-----------------------------------------------------------------------------------------| | class | HTML class(es) added to the `` and `` tags | | cell_class | HTML class(es) added only to the `` cell tag | | head_class | HTML class(es) added only to the `` head tag | | title | Custom title that is put into the ``. By default the capitalized field name is used. | | precision | For decimal types this sets the number of digits after the decimal point | | renderer | Custom cell render component | | skip | Don't render this column | ### Per-table options | Option | Description | |:-------------------|:----------------------------------------------------------------------------------| | row_class | HTML class(es) added to the `` row tags except the first one (the header row) | | head_row_class | Added only to the first `` (the header row) | | tag | The HTML tag name used for the root of the table. Defaults to `"table"`. | | row_renderer | Custom row renderer component | | head_cell_renderer | Custom head cell renderer component | | dyn_row_classes | Enables reactive row classes through the method `row_classes()` | ### Dynamic row classes A simple way to give feedback to interactions is to add a class to a row element. This makes is easy to highlight a selected row for example. Above the `struct` enable the `dyn_row_classes` option. ```rust #[derive(PartialEq, TableData)] #[table(dyn_row_classes)] struct Hotel { ... } ``` What classes are added to a row is determined by calling the `row_classes()` method. So let's implement it. ```rust impl Hotel { fn row_classes(&self, index: usize, cx: Scope) -> Vec { if /* this hotel is selected */ { vec!["selected".to_string()] } else { vec![] } } } ``` The method `row_classes()` is called for each row. It receives the `index` of the row and the current context. Please look at the hotel example in the `examples` directory for a full example. ### Custom renderers Custom renderers are a powerful way to customize almost all aspects of the table rendering. Yet they are very easy to use. #### Custom cell renderer Probably the most common use for a custom renderer is to customize the representation of a value in a table cell. Let's say we have a table of books with a `title` and a `rating` field. The `rating` field is an integer number from 1 to 5 that represents the number of stars a book has received. ```rust #[derive(PartialEq, TableData)] pub struct Book { pub title: i32, pub rating: u8, } ``` With the default renderer we only see a number in the "Rating" column. If we want to display this number as stars we can write a custom renderer. ```rust #[derive(PartialEq, TableData)] pub struct Book { pub title: i32, #[table(cell_renderer = "StarRenderer")] // specify the custom renderer pub rating: u8, } // The actual renderer component. It has to accept the DefaultTableCellProps. pub fn StarRenderer(cx: Scope>) -> Element { // the value of the rating field is provided as cx.props.value here let count = cx.props.value as usize; // create a string with #count filled stars. let mut stars = "".to_owned(); for _ in 0..count { stars += "★"; } // then fill up the rest of the 5 stars with emtpy stars for _ in count..5 { stars += "☆"; } // display the string cx.render(rsx! { td { class: "{cx.props.class}", "{stars}" } }) } ``` Now the rating is properly displayed as stars. To see this in action run the hotels example in the `examples/` folder. #### Custom row and head cell renderers They work basically the same as the cell renderers but are specified above the struct definition. ```rust #[derive(PartialEq, TableData)] #[table(row_renderer = "MyRowRenderer", head_cell_renderer = "MyHeadCellRenderer")] pub struct Book { pub title: i32, pub rating: u8, } ``` To see how to implement them please refer to the default renderers in src/cell_renderers.rs and src/row_enderers.rs. The easiest way to get going is to copy and paste the respective default renderer and customize from there.