use dioxus::prelude::*;
#[derive(Clone, Debug, PartialEq)]
pub struct PendingAttachment {
pub file_name: String,
pub file_size: u64,
pub mime_type: String,
pub is_uploading: bool,
}
#[component]
pub fn AttachmentBar(
attachments: Vec<PendingAttachment>,
on_remove: EventHandler<usize>,
on_clear_all: EventHandler<()>,
) -> Element {
if attachments.is_empty() {
return rsx! {};
}
rsx! {
div {
class: "attachment-bar",
div {
class: "attachment-bar__header",
span {
class: "attachment-bar__count",
"{attachments.len()} file(s) attached"
}
button {
class: "attachment-bar__clear-btn",
onclick: move |_| on_clear_all.call(()),
"Clear all"
}
}
div {
class: "attachment-bar__list",
for (idx, att) in attachments.iter().enumerate() {
{
let file_name = att.file_name.clone();
let size_text = format_size(att.file_size);
let is_uploading = att.is_uploading;
let icon = mime_icon(&att.mime_type);
rsx! {
div {
class: if is_uploading { "attachment-bar__item attachment-bar__item--uploading" } else { "attachment-bar__item" },
span { class: "attachment-bar__item-icon", "{icon}" }
div {
class: "attachment-bar__item-info",
span { class: "attachment-bar__item-name", "{file_name}" }
span { class: "attachment-bar__item-size", "{size_text}" }
}
if is_uploading {
span { class: "attachment-bar__item-spinner", "..." }
} else {
button {
class: "attachment-bar__item-remove",
title: "Remove",
onclick: move |_| on_remove.call(idx),
"✕"
}
}
}
}
}
}
}
}
}
}
fn format_size(bytes: u64) -> String {
if bytes >= 1_048_576 {
format!("{:.1} MB", bytes as f64 / 1_048_576.0)
} else if bytes >= 1_024 {
format!("{:.1} KB", bytes as f64 / 1_024.0)
} else {
format!("{bytes} B")
}
}
fn mime_icon(mime: &str) -> &'static str {
if mime.starts_with("image/") {
"🖼"
} else if mime.starts_with("video/") {
"🎬"
} else if mime.starts_with("audio/") {
"🎵"
} else if mime.contains("pdf") {
"📕"
} else {
"📄"
}
}