dioxus_bootstrap_css/
toast.rs1use dioxus::prelude::*;
2
3use crate::types::Color;
4
5#[derive(Clone, PartialEq, Props)]
41pub struct ToastProps {
42 pub show: Signal<bool>,
44 #[props(default)]
46 pub title: String,
47 #[props(default)]
49 pub subtitle: String,
50 #[props(default = true)]
52 pub show_close: bool,
53 #[props(default)]
55 pub color: Option<Color>,
56 #[props(default)]
58 pub class: String,
59 pub children: Element,
61}
62
63#[component]
64pub fn Toast(props: ToastProps) -> Element {
65 let is_shown = *props.show.read();
66 let mut show_signal = props.show;
67
68 if !is_shown {
69 return rsx! {};
70 }
71
72 let color_class = match &props.color {
73 Some(c) => format!(" text-bg-{c}"),
74 None => String::new(),
75 };
76
77 let full_class = if props.class.is_empty() {
78 format!("toast show{color_class}")
79 } else {
80 format!("toast show{color_class} {}", props.class)
81 };
82
83 rsx! {
84 div {
85 class: "{full_class}",
86 role: "alert",
87 "aria-live": "assertive",
88 "aria-atomic": "true",
89 if !props.title.is_empty() {
90 div { class: "toast-header",
91 strong { class: "me-auto", "{props.title}" }
92 if !props.subtitle.is_empty() {
93 small { "{props.subtitle}" }
94 }
95 if props.show_close {
96 button {
97 class: "btn-close",
98 r#type: "button",
99 "aria-label": "Close",
100 onclick: move |_| show_signal.set(false),
101 }
102 }
103 }
104 }
105 div { class: "toast-body", {props.children} }
106 }
107 }
108}
109
110#[derive(Clone, PartialEq, Props)]
121pub struct ToastContainerProps {
122 #[props(default)]
124 pub position: ToastPosition,
125 #[props(default)]
127 pub class: String,
128 pub children: Element,
130}
131
132#[derive(Clone, Copy, Debug, Default, PartialEq)]
134pub enum ToastPosition {
135 TopStart,
136 TopCenter,
137 #[default]
138 TopEnd,
139 MiddleCenter,
140 BottomStart,
141 BottomCenter,
142 BottomEnd,
143}
144
145impl std::fmt::Display for ToastPosition {
146 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
147 match self {
148 ToastPosition::TopStart => write!(f, "top-0 start-0"),
149 ToastPosition::TopCenter => write!(f, "top-0 start-50 translate-middle-x"),
150 ToastPosition::TopEnd => write!(f, "top-0 end-0"),
151 ToastPosition::MiddleCenter => {
152 write!(f, "top-50 start-50 translate-middle")
153 }
154 ToastPosition::BottomStart => write!(f, "bottom-0 start-0"),
155 ToastPosition::BottomCenter => {
156 write!(f, "bottom-0 start-50 translate-middle-x")
157 }
158 ToastPosition::BottomEnd => write!(f, "bottom-0 end-0"),
159 }
160 }
161}
162
163#[component]
164pub fn ToastContainer(props: ToastContainerProps) -> Element {
165 let pos = props.position;
166 let full_class = if props.class.is_empty() {
167 format!("toast-container position-fixed p-3 {pos}")
168 } else {
169 format!("toast-container position-fixed p-3 {pos} {}", props.class)
170 };
171
172 rsx! {
173 div { class: "{full_class}", {props.children} }
174 }
175}