dioxus_bootstrap_css/
toast.rs1use dioxus::prelude::*;
2
3use crate::types::Color;
4
5#[derive(Clone, PartialEq, Props)]
16pub struct ToastProps {
17 pub show: Signal<bool>,
19 #[props(default)]
21 pub title: String,
22 #[props(default)]
24 pub subtitle: String,
25 #[props(default = true)]
27 pub show_close: bool,
28 #[props(default)]
30 pub color: Option<Color>,
31 #[props(default)]
33 pub class: String,
34 pub children: Element,
36}
37
38#[component]
39pub fn Toast(props: ToastProps) -> Element {
40 let is_shown = *props.show.read();
41 let mut show_signal = props.show;
42
43 if !is_shown {
44 return rsx! {};
45 }
46
47 let color_class = match &props.color {
48 Some(c) => format!(" text-bg-{c}"),
49 None => String::new(),
50 };
51
52 let full_class = if props.class.is_empty() {
53 format!("toast show{color_class}")
54 } else {
55 format!("toast show{color_class} {}", props.class)
56 };
57
58 rsx! {
59 div {
60 class: "{full_class}",
61 role: "alert",
62 "aria-live": "assertive",
63 "aria-atomic": "true",
64 if !props.title.is_empty() {
65 div { class: "toast-header",
66 strong { class: "me-auto", "{props.title}" }
67 if !props.subtitle.is_empty() {
68 small { "{props.subtitle}" }
69 }
70 if props.show_close {
71 button {
72 class: "btn-close",
73 r#type: "button",
74 "aria-label": "Close",
75 onclick: move |_| show_signal.set(false),
76 }
77 }
78 }
79 }
80 div { class: "toast-body", {props.children} }
81 }
82 }
83}
84
85#[derive(Clone, PartialEq, Props)]
96pub struct ToastContainerProps {
97 #[props(default)]
99 pub position: ToastPosition,
100 #[props(default)]
102 pub class: String,
103 pub children: Element,
105}
106
107#[derive(Clone, Copy, Debug, Default, PartialEq)]
109pub enum ToastPosition {
110 TopStart,
111 TopCenter,
112 #[default]
113 TopEnd,
114 MiddleCenter,
115 BottomStart,
116 BottomCenter,
117 BottomEnd,
118}
119
120impl std::fmt::Display for ToastPosition {
121 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
122 match self {
123 ToastPosition::TopStart => write!(f, "top-0 start-0"),
124 ToastPosition::TopCenter => write!(f, "top-0 start-50 translate-middle-x"),
125 ToastPosition::TopEnd => write!(f, "top-0 end-0"),
126 ToastPosition::MiddleCenter => {
127 write!(f, "top-50 start-50 translate-middle")
128 }
129 ToastPosition::BottomStart => write!(f, "bottom-0 start-0"),
130 ToastPosition::BottomCenter => {
131 write!(f, "bottom-0 start-50 translate-middle-x")
132 }
133 ToastPosition::BottomEnd => write!(f, "bottom-0 end-0"),
134 }
135 }
136}
137
138#[component]
139pub fn ToastContainer(props: ToastContainerProps) -> Element {
140 let pos = props.position;
141 let full_class = if props.class.is_empty() {
142 format!("toast-container position-fixed p-3 {pos}")
143 } else {
144 format!("toast-container position-fixed p-3 {pos} {}", props.class)
145 };
146
147 rsx! {
148 div { class: "{full_class}", {props.children} }
149 }
150}