yew_notifications/
provider.rs1use std::marker::PhantomData;
2
3use yew::{
4 classes, function_component, html, use_effect_with, use_reducer_eq, Callback, Children, Classes, ContextProvider,
5 Html, Properties,
6};
7
8use crate::manager::{Action, NotificationsList};
9use crate::{Notifiable, NotifiableComponentFactory, NotificationsManager};
10
11const NOTIFICATION_PROVIDER_STYLE: &str = include_str!("../static/notifications_provider.scss");
12
13#[derive(Debug, Clone, PartialEq)]
15pub enum NotificationsPosition {
16 TopLeft,
18 TopRight,
20 BottomRight,
22 BottomLeft,
24 Custom(Classes),
29}
30
31impl From<&NotificationsPosition> for Vec<Classes> {
32 fn from(position: &NotificationsPosition) -> Self {
33 let position = match position {
34 NotificationsPosition::TopLeft => classes!("notifications-provider-top-left"),
35 NotificationsPosition::TopRight => classes!("notifications-provider-top-right"),
36 NotificationsPosition::BottomRight => classes!("notifications-provider-bottom-right"),
37 NotificationsPosition::BottomLeft => classes!("notifications-provider-bottom-left"),
38 NotificationsPosition::Custom(classes) => return vec![classes.clone()],
39 };
40 vec![classes!("notifications"), position]
41 }
42}
43
44impl From<&str> for NotificationsPosition {
45 fn from(position: &str) -> Self {
46 match position {
47 "top-left" => Self::TopLeft,
48 "top-right" => Self::TopRight,
49 "bottom-left" => Self::BottomLeft,
50 "bottom-right" => Self::BottomRight,
51 p => Self::Custom(classes!(p.to_owned())),
52 }
53 }
54}
55
56#[derive(Properties, PartialEq, Clone)]
58pub struct NotificationsProviderProps<N: Notifiable + PartialEq, F: NotifiableComponentFactory<N> + PartialEq + Clone> {
59 pub children: Children,
61 pub component_creator: F,
63 #[prop_or(NotificationsPosition::BottomRight)]
67 pub position: NotificationsPosition,
68 #[prop_or_default]
69 pub _notification: PhantomData<N>,
70}
71
72#[function_component(NotificationsProvider)]
90pub fn notifications_provider<
91 N: Notifiable + PartialEq + Clone,
92 F: NotifiableComponentFactory<N> + PartialEq + Clone,
93>(
94 props: &NotificationsProviderProps<N, F>,
95) -> Html {
96 let notifications = use_reducer_eq(NotificationsList::<N>::default);
97
98 let manager = NotificationsManager {
99 sender: Some(notifications.dispatcher()),
100 };
101
102 use_effect_with(
103 (!notifications.is_empty(), notifications.dispatcher()),
104 |(is_active, sender)| {
105 use gloo::timers::callback::Interval;
106
107 let sender = sender.clone();
108 let is_active = *is_active;
109
110 let interval = Interval::new(NotificationsList::<N>::TIME_TICK_MILLIS as u32, move || {
111 if is_active {
112 sender.dispatch(Action::Tick);
113 }
114 });
115
116 move || drop(interval)
117 },
118 );
119
120 let ns = notifications.notifications.clone();
121 let children = props.children.clone();
122 let dispatcher = notifications.dispatcher();
123
124 let notification_creator = &props.component_creator;
125
126 let classes: Vec<Classes> = (&props.position).into();
127
128 let notification_style = {
129 #[cfg(feature = "standard-notification")]
130 {
131 include_str!("../static/notification.scss")
132 }
133
134 #[cfg(not(feature = "standard-notification"))]
135 {
136 ""
137 }
138 };
139
140 html! {
141 <ContextProvider<NotificationsManager<N>> context={manager}>
142 {children}
143 <style>
144 {NOTIFICATION_PROVIDER_STYLE}
145 {notification_style}
146 </style>
147 <div class={classes}>
148 {for ns.iter().map(|n| {
149 let notification = n.clone();
150 let id = notification.id();
151
152 let onclick = {
153 let dispatcher = dispatcher.clone();
154 Callback::from(move |_| {
155 dispatcher.dispatch(Action::Close(id));
156 })
157 };
158
159 let onenter = {
160 let dispatcher = dispatcher.clone();
161 Callback::from(move |_| {
162 dispatcher.dispatch(Action::Pause(id));
163 })
164 };
165
166 let onleave = {
167 let dispatcher = dispatcher.clone();
168 Callback::from(move |_| {
169 dispatcher.dispatch(Action::Continue(id));
170 })
171 };
172
173 notification_creator.component(notification, onclick, onenter, onleave)
174 })}
175 </div>
176 </ContextProvider<NotificationsManager<N>>>
177 }
178}