Skip to content
37 changes: 21 additions & 16 deletions src/dioxus/body.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ use std::collections::HashMap;
/// - `loading`: A `bool` flag that, when true, displays a loading message instead of data rows.
/// - `classes`: A `TableClasses` struct for customizing the CSS class names of the body, rows, and cells.
/// - `texts`: A `TableTexts` struct that provides custom text for the loading and empty states.
/// - `on_column_click`: An optional callback when a row is clicked.
///
/// # Behavior
/// - If `loading` is `true`, a single row with a loading message is shown spanning all columns.
Expand Down Expand Up @@ -65,31 +66,39 @@ pub fn TableBody(
loading: bool,
classes: TableClasses,
texts: TableTexts,
on_column_click: Option<EventHandler<HashMap<&'static str, String>>>,
) -> Element {
let content = if loading {
rsx! {
tr { class: "{classes.loading_row}",
td {
colspan: "{columns.len()}",
"{texts.loading}"
}
td { colspan: "{columns.len()}", "{texts.loading}" }
}
}
} else if rows.is_empty() {
rsx! {
tr { class: "{classes.empty_row}",
td {
colspan: "{columns.len()}",
"{texts.empty}"
}
td { colspan: "{columns.len()}", "{texts.empty}" }
}
}
} else {
rsx! {
for row in rows.iter() {
tr { class: "{classes.row}", role: "row",
tr {
class: "{classes.row}",
role: "row",
onclick: {
let row1 = row.clone();
move |_| {
if let Some(on_column_click) = on_column_click {
on_column_click.call(row1.clone());
}
}
},
for col in columns.iter() {
td { class: "{classes.body_cell}", role: "cell",
td {
class: "{classes.body_cell} {col.cell_class.unwrap_or_default()}",
style: "{col.cell_style.unwrap_or_default()}",
role: "cell",
BodyCell {
column: col.clone(),
content: row.get(col.id).unwrap_or(&String::new()),
Expand All @@ -102,9 +111,7 @@ pub fn TableBody(
};

rsx! {
tbody { class: "{classes.tbody}",
{content}
}
tbody { class: "{classes.tbody}", {content} }
}
}

Expand All @@ -113,8 +120,6 @@ fn BodyCell(column: Column, content: String) -> Element {
if let Some(cb) = column.cell {
cb(content)
} else {
rsx! {
"{content}"
}
rsx! { "{content}" }
}
}
38 changes: 22 additions & 16 deletions src/dioxus/table.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
use dioxus::prelude::*;
#[cfg(target_family = "wasm")]
use web_sys::UrlSearchParams;
#[cfg(target_family = "wasm")]
use web_sys::wasm_bindgen::JsValue;

use crate::dioxus::body::TableBody;
Expand Down Expand Up @@ -78,10 +80,12 @@ pub fn Table(props: TableProps) -> Element {
search,
texts,
classes,
default_sort_column,
on_column_click,
} = props;

let mut page = use_signal(|| 0_usize);
let mut sort_column = use_signal(|| None::<&'static str>);
let mut sort_column = use_signal(|| default_sort_column);
let mut sort_order = use_signal(SortOrder::default);
let mut search_query = use_signal(String::new);

Expand Down Expand Up @@ -146,12 +150,15 @@ pub fn Table(props: TableProps) -> Element {
let end = ((page() + 1) * page_size).min(filtered_rows.len());
let page_rows = &filtered_rows[start..end];

// if this is the first click on a header column, sort ASC
// on second cick sort DESC
// on third click don't sort anymore
let on_sort_column = move |id: &'static str| {
if Some(id) == sort_column() {
sort_order.set(match sort_order() {
SortOrder::Asc => SortOrder::Desc,
SortOrder::Desc => SortOrder::Asc,
});
match sort_order() {
SortOrder::Asc => sort_order.set(SortOrder::Desc),
SortOrder::Desc => sort_column.set(None),
}
} else {
sort_column.set(Some(id));
sort_order.set(SortOrder::Asc);
Expand All @@ -161,8 +168,8 @@ pub fn Table(props: TableProps) -> Element {
let pagination_controls = if paginate {
rsx! {
PaginationControls {
page: page,
total_pages: total_pages,
page,
total_pages,
classes: classes.clone(),
texts: texts.clone(),
}
Expand All @@ -172,8 +179,7 @@ pub fn Table(props: TableProps) -> Element {
};

rsx! {
div {
class: "{classes.container}",
div { class: "{classes.container}",
if search {
input {
class: "{classes.search_input}",
Expand All @@ -186,24 +192,24 @@ pub fn Table(props: TableProps) -> Element {
page.set(0);
#[cfg(target_family = "wasm")]
update_search_param(&val);
}
},
}
}
table {
class: "{classes.table}",
table { class: "{classes.table}",
TableHeader {
columns: columns.clone(),
sort_column: sort_column,
sort_order: sort_order,
on_sort_column: on_sort_column,
sort_column,
sort_order,
on_sort_column,
classes: classes.clone(),
}
TableBody {
columns: columns.clone(),
rows: page_rows.to_vec(),
loading: loading,
loading,
classes: classes.clone(),
texts: texts.clone(),
on_column_click,
}
}
{pagination_controls}
Expand Down
17 changes: 17 additions & 0 deletions src/dioxus/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,14 @@ pub struct Column {
/// Optional CSS classes for the column header.
#[props(default)]
pub class: Option<&'static str>,

/// Optional inline styles for the column cell.
#[props(default)]
pub cell_style: Option<&'static str>,

/// Optional CSS classes for the column cell.
#[props(default)]
pub cell_class: Option<&'static str>,
}

/// Text labels for table UI elements.
Expand Down Expand Up @@ -165,6 +173,15 @@ pub struct TableProps {
/// CSS classes for styling different parts of the table.
#[props(default)]
pub classes: TableClasses,

/// Default sort column when the table is initially rendered
#[props(default)]
pub default_sort_column: Option<&'static str>,

/// optional event handler when a column has been clicked
/// will pass in the content of the column
#[props(default)]
pub on_column_click: Option<EventHandler<HashMap<&'static str, String>>>,
}

/// Sort direction (ascending or descending).
Expand Down
Loading