1use std::cell::Cell;
2use std::panic::PanicHookInfo as PanicInfo;
3use std::rc::Rc;
4
5use web_sys::Element;
6
7use crate::app_handle::AppHandle;
8use crate::html::BaseComponent;
9
10thread_local! {
11 static PANIC_HOOK_IS_SET: Cell<bool> = const { Cell::new(false) };
12}
13
14#[cfg(feature = "csr")]
18#[allow(clippy::incompatible_msrv)]
19pub fn set_custom_panic_hook(hook: Box<dyn Fn(&PanicInfo<'_>) + Sync + Send + 'static>) {
20 std::panic::set_hook(hook);
21 PANIC_HOOK_IS_SET.with(|hook_is_set| hook_is_set.set(true));
22}
23
24fn set_default_panic_hook() {
25 if std::thread::panicking() {
26 return;
28 }
29 if !PANIC_HOOK_IS_SET.with(|hook_is_set| hook_is_set.replace(true)) {
30 std::panic::set_hook(Box::new(console_error_panic_hook::hook));
31 }
32}
33
34#[cfg(feature = "csr")]
38#[derive(Debug)]
39#[must_use = "Renderer does nothing unless render() is called."]
40pub struct Renderer<COMP>
41where
42 COMP: BaseComponent + 'static,
43{
44 root: Element,
45 props: COMP::Properties,
46}
47
48impl<COMP> Default for Renderer<COMP>
49where
50 COMP: BaseComponent + 'static,
51 COMP::Properties: Default,
52{
53 fn default() -> Self {
54 Self::with_props(Default::default())
55 }
56}
57
58impl<COMP> Renderer<COMP>
59where
60 COMP: BaseComponent + 'static,
61 COMP::Properties: Default,
62{
63 pub fn new() -> Self {
65 Self::default()
66 }
67
68 pub fn with_root(root: Element) -> Self {
70 Self::with_root_and_props(root, Default::default())
71 }
72}
73
74impl<COMP> Renderer<COMP>
75where
76 COMP: BaseComponent + 'static,
77{
78 pub fn with_props(props: COMP::Properties) -> Self {
80 Self::with_root_and_props(
81 gloo::utils::document()
82 .body()
83 .expect("no body node found")
84 .into(),
85 props,
86 )
87 }
88
89 pub fn with_root_and_props(root: Element, props: COMP::Properties) -> Self {
91 Self { root, props }
92 }
93
94 pub fn render(self) -> AppHandle<COMP> {
96 set_default_panic_hook();
97 AppHandle::<COMP>::mount_with_props(self.root, Rc::new(self.props))
98 }
99}
100
101#[cfg(feature = "hydration")]
102mod feat_hydration {
103 use super::*;
104
105 impl<COMP> Renderer<COMP>
106 where
107 COMP: BaseComponent + 'static,
108 {
109 pub fn hydrate(self) -> AppHandle<COMP> {
111 set_default_panic_hook();
112 AppHandle::<COMP>::hydrate_with_props(self.root, Rc::new(self.props))
113 }
114 }
115}