email_components/
render.rs1use stylist::{
2 manager::{StyleManager, render_static},
3 yew::ManagerProvider,
4};
5use yew::{BaseComponent, Html, Properties, ServerRenderer, function_component, html};
6
7use crate::email_html::HEAD_PLACEHOLDER;
8
9pub const DOCTYPE: &str = r#"<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">"#;
10
11pub async fn render<C>(props: C::Properties) -> String
12where
13 C: BaseComponent,
14 C::Properties: Send + Clone,
15{
16 let (writer, reader) = render_static();
17
18 let renderer = ServerRenderer::<Wrapped<C>>::with_props(|| {
19 let style_manager = StyleManager::builder()
20 .writer(writer)
21 .build()
22 .expect("failed to create style manager");
23 Props {
24 style_manager,
25 inner_props: props,
26 }
27 });
28 let rendered = renderer.render().await;
29
30 let style_data = reader.read_style_data();
31 let mut styles = String::new();
32 style_data
33 .write_static_markup(&mut styles)
34 .expect("failed to read styles from style manager");
35
36 let rendered_with_styles = rendered.replace(HEAD_PLACEHOLDER, &styles);
37
38 format!("{DOCTYPE}\n{rendered_with_styles}")
39}
40
41#[derive(Properties, PartialEq)]
42struct Props<P>
43where
44 P: PartialEq,
45{
46 pub style_manager: StyleManager,
47 pub inner_props: P,
48}
49
50#[function_component]
51fn Wrapped<C>(props: &Props<C::Properties>) -> Html
52where
53 C: BaseComponent,
54 C::Properties: Clone,
55{
56 let Props {
57 style_manager,
58 inner_props,
59 } = props;
60
61 html! {
62 <ManagerProvider manager={style_manager.clone()}>
63 <C ..inner_props.clone() />
64 </ManagerProvider>
65 }
66}