flams_web_utils/
lib.rs

1#![cfg_attr(docsrs, feature(doc_auto_cfg))]
2#![recursion_limit = "256"]
3
4pub mod components;
5pub mod mathml;
6
7use std::borrow::Cow;
8
9use flams_utils::{hashstr, CSS};
10
11#[cfg(feature = "ssr")]
12#[derive(Default, Clone)]
13pub struct CssIds(
14    flams_utils::triomphe::Arc<
15        flams_utils::parking_lot::Mutex<flams_utils::vecmap::VecSet<Cow<'static, str>>>,
16    >,
17);
18
19#[cfg(feature = "ssr")]
20/// #### Errors
21pub async fn blocking_server_fn<T: Send + 'static>(
22    f: impl FnOnce() -> Result<T, String> + Send + 'static,
23) -> Result<T, leptos::prelude::ServerFnError<String>> {
24    tokio::task::spawn_blocking(f)
25        .await
26        .map_err(|e| e.to_string())?
27        .map_err(Into::into)
28}
29
30pub fn do_css(css: CSS) {
31    match css {
32        CSS::Inline(s) => {
33            let id = hashstr("id_", &s);
34            #[cfg(not(target_family = "wasm"))]
35            let s = String::from(s);
36            do_inject_css(id.into(), s.into());
37        }
38        CSS::Class { name, css } => {
39            #[cfg(not(target_family = "wasm"))]
40            let name = String::from(name);
41            #[cfg(not(target_family = "wasm"))]
42            let css = String::from(css);
43            do_inject_css(name.into(), css.into());
44        }
45        CSS::Link(s) => {
46            let id = hashstr("id_", &s);
47            #[cfg(feature = "ssr")]
48            {
49                use leptos::prelude::expect_context;
50                use leptos_meta::Stylesheet;
51                let ids = expect_context::<CssIds>();
52                let mut ids = ids.0.lock();
53                if !ids.0.contains(&std::borrow::Cow::Borrowed(&id)) {
54                    ids.insert(id.clone().into());
55                    let _ = leptos::view! {
56                        <Stylesheet id=id href=s.to_string()/>
57                    };
58                }
59                drop(ids);
60            }
61            #[cfg(all(any(feature = "hydrate", feature = "csr"), not(feature = "ssr")))]
62            {
63                use leptos::prelude::document;
64                let Some(head) = document().head() else {
65                    leptos::logging::log!("ERROR: head does not exist");
66                    return;
67                };
68                match head.query_selector(&format!("link#{id}")) {
69                    Ok(Some(_)) => return,
70                    Err(e) => {
71                        leptos::logging::log!("ERROR: query link element error: {e:?}");
72                        return;
73                    }
74                    Ok(None) => (),
75                };
76                let Ok(style) = document().create_element("link") else {
77                    leptos::logging::log!("ERROR: error creating style element");
78                    return;
79                };
80                _ = style.set_attribute("id", &id);
81                _ = style.set_attribute("rel", "stylesheet");
82                _ = style.set_attribute("href", &s);
83                _ = head.prepend_with_node_1(&style);
84            }
85        }
86    }
87}
88
89#[inline]
90pub fn inject_css(id: &'static str, content: &'static str) {
91    do_inject_css(Cow::Borrowed(id), Cow::Borrowed(content));
92}
93
94#[macro_export]
95macro_rules! console_log {
96    () => {};
97    ($arg:expr) => {
98        ::web_sys::console::log_1(&::web_sys::js_sys::JsValue::from($l))
99    };
100    ($arg1:expr,$arg2:expr) => {
101        ::web_sys::console::log_2(
102            &::web_sys::js_sys::JsValue::from($l),
103            &::web_sys::js_sys::JsValue::from($l),
104        )
105    };
106}
107
108#[allow(clippy::missing_const_for_fn)]
109#[allow(clippy::needless_pass_by_value)]
110fn do_inject_css(id: Cow<'static, str>, content: Cow<'static, str>) {
111    #[cfg(feature = "ssr")]
112    {
113        use leptos_meta::Style;
114
115        use leptos::prelude::expect_context;
116        let ids = expect_context::<CssIds>();
117        let mut ids = ids.0.lock();
118        if !ids.0.contains(&id) {
119            ids.insert(id.clone());
120            let _ = leptos::view! {
121                <Style id=id>
122                    {content}
123                </Style>
124            };
125        }
126        drop(ids);
127    }
128    #[cfg(not(feature = "ssr"))]
129    {
130        use leptos::prelude::document;
131        let Some(head) = document().head() else {
132            leptos::logging::log!("ERROR: head does not exist");
133            return;
134        };
135        let Ok(style) = head.query_selector(&format!("style#{id}")) else {
136            leptos::logging::log!("ERROR: query style element error");
137            return;
138        };
139        if style.is_some() {
140            return;
141        }
142
143        let Ok(style) = document().create_element("style") else {
144            leptos::logging::log!("ERROR: error creating style element");
145            return;
146        };
147        _ = style.set_attribute("id", &id);
148        style.set_text_content(Some(&content));
149        _ = head.prepend_with_node_1(&style);
150    }
151}
152
153//#[cfg(any(feature = "csr", feature = "ssr"))]
154pub fn try_catch<R>(run: impl FnOnce() -> R) -> Result<R, leptos::wasm_bindgen::JsError> {
155    std::panic::catch_unwind(std::panic::AssertUnwindSafe(run)).map_err(|e| {
156        if let Some(s) = e.downcast_ref::<&str>() {
157            return leptos::wasm_bindgen::JsError::new(*s);
158        }
159        if let Some(s) = e.downcast_ref::<String>() {
160            return leptos::wasm_bindgen::JsError::new(s);
161        }
162        leptos::wasm_bindgen::JsError::new("Box<dyn Error>")
163    })
164}
165
166#[cfg(feature = "ssr")]
167pub use http;
168#[cfg(feature = "ssr")]
169pub use leptos_axum;
170
171#[cfg(feature = "ssr")]
172#[macro_export]
173macro_rules! not_found{
174    (! $($e:tt)*) => { {
175        let response = expect_context::<$crate::leptos_axum::ResponseOptions>();
176        response.set_status($crate::http::StatusCode::NOT_FOUND);
177        format!($($e)*).into()
178    }};
179    ($($e:tt)*) => { return Err(not_found!(! $($e)*))};
180}