dioxus_document/
document.rs1use std::sync::Arc;
2
3use super::*;
4
5pub type DocumentContext = Arc<dyn Document>;
7
8fn format_string_for_js(s: &str) -> String {
9 let escaped = s
10 .replace('\\', "\\\\")
11 .replace('\n', "\\n")
12 .replace('\r', "\\r")
13 .replace('"', "\\\"");
14 format!("\"{escaped}\"")
15}
16
17fn format_attributes(attributes: &[(&str, String)]) -> String {
18 let mut formatted = String::from("[");
19 for (key, value) in attributes {
20 formatted.push_str(&format!(
21 "[{}, {}],",
22 format_string_for_js(key),
23 format_string_for_js(value)
24 ));
25 }
26 if formatted.ends_with(',') {
27 formatted.pop();
28 }
29 formatted.push(']');
30 formatted
31}
32
33pub fn create_element_in_head(
37 tag: &str,
38 attributes: &[(&str, String)],
39 children: Option<String>,
40) -> String {
41 let helpers = include_str!("./js/head.js");
42 let attributes = format_attributes(attributes);
43 let children = children
44 .as_deref()
45 .map(format_string_for_js)
46 .unwrap_or("null".to_string());
47 let tag = format_string_for_js(tag);
48 format!(r#"{helpers};window.createElementInHead({tag}, {attributes}, {children});"#)
49}
50
51pub trait Document: 'static {
65 fn eval(&self, js: String) -> Eval;
67
68 fn set_title(&self, title: String) {
70 self.eval(format!("document.title = {title:?};"));
71 }
72
73 fn create_head_element(
75 &self,
76 name: &str,
77 attributes: &[(&str, String)],
78 contents: Option<String>,
79 ) {
80 self.eval(create_element_in_head(name, attributes, contents));
83 }
84
85 fn create_meta(&self, props: MetaProps) {
87 let attributes = props.attributes();
88 self.create_head_element("meta", &attributes, None);
89 }
90
91 fn create_script(&self, props: ScriptProps) {
93 let attributes = props.attributes();
94 self.create_head_element("script", &attributes, props.script_contents().ok());
95 }
96
97 fn create_style(&self, props: StyleProps) {
99 let attributes = props.attributes();
100 self.create_head_element("style", &attributes, props.style_contents().ok());
101 }
102
103 fn create_link(&self, props: LinkProps) {
105 let attributes = props.attributes();
106 self.create_head_element("link", &attributes, None);
107 }
108
109 fn create_head_component(&self) -> bool {
113 true
114 }
115}
116
117#[derive(Default)]
119pub struct NoOpDocument;
120
121impl Document for NoOpDocument {
122 fn eval(&self, _: String) -> Eval {
123 let owner = generational_box::Owner::default();
124 struct NoOpEvaluator;
125 impl Evaluator for NoOpEvaluator {
126 fn poll_join(
127 &mut self,
128 _: &mut std::task::Context<'_>,
129 ) -> std::task::Poll<Result<serde_json::Value, EvalError>> {
130 std::task::Poll::Ready(Err(EvalError::Unsupported))
131 }
132
133 fn poll_recv(
134 &mut self,
135 _: &mut std::task::Context<'_>,
136 ) -> std::task::Poll<Result<serde_json::Value, EvalError>> {
137 std::task::Poll::Ready(Err(EvalError::Unsupported))
138 }
139
140 fn send(&self, _data: serde_json::Value) -> Result<(), EvalError> {
141 Err(EvalError::Unsupported)
142 }
143 }
144 Eval::new(owner.insert(Box::new(NoOpEvaluator)))
145 }
146
147 fn set_title(&self, _: String) {}
148 fn create_meta(&self, _: MetaProps) {}
149 fn create_script(&self, _: ScriptProps) {}
150 fn create_style(&self, _: StyleProps) {}
151 fn create_link(&self, _: LinkProps) {}
152}