1use crate::events::{BlitzKeyboardData, NativeClickData, NativeConverter, NativeFormData};
3use crate::mutation_writer::{DioxusState, MutationWriter};
4use crate::qual_name;
5use crate::NodeId;
6use blitz_dom::{
7 Attribute, BaseDocument, Document, DocumentConfig, EventDriver, EventHandler, Node, DEFAULT_CSS,
8};
9use blitz_traits::events::{DomEvent, DomEventData, EventState, UiEvent};
10use dioxus_core::{ElementId, Event, VirtualDom};
11use dioxus_html::{set_event_converter, PlatformEventData};
12use futures_util::task::noop_waker;
13use futures_util::{pin_mut, FutureExt};
14use std::ops::{Deref, DerefMut};
15use std::sync::LazyLock;
16use std::task::{Context as TaskContext, Waker};
17use std::{any::Any, rc::Rc};
18
19fn wrap_event_data<T: Any>(value: T) -> Rc<dyn Any> {
20 Rc::new(PlatformEventData::new(Box::new(value)))
21}
22
23fn get_dioxus_id(node: &Node) -> Option<ElementId> {
25 node.element_data()?
26 .attrs
27 .iter()
28 .find(|attr| *attr.name.local == *"data-dioxus-id")
29 .and_then(|attr| attr.value.parse::<usize>().ok())
30 .map(ElementId)
31}
32
33pub struct DioxusDocument {
62 pub inner: BaseDocument,
63 pub vdom: VirtualDom,
64 pub vdom_state: DioxusState,
65
66 #[allow(unused)]
67 pub(crate) html_element_id: NodeId,
68 #[allow(unused)]
69 pub(crate) head_element_id: NodeId,
70 #[allow(unused)]
71 pub(crate) body_element_id: NodeId,
72 #[allow(unused)]
73 pub(crate) main_element_id: NodeId,
74}
75
76impl DioxusDocument {
77 pub fn new(vdom: VirtualDom, mut config: DocumentConfig) -> Self {
79 set_event_converter(Box::new(NativeConverter {}));
81
82 config.base_url = Some(
83 config
84 .base_url
85 .unwrap_or_else(|| String::from("dioxus://index.html")),
86 );
87 let mut doc = BaseDocument::new(config);
88
89 doc.add_user_agent_stylesheet(DEFAULT_CSS);
91
92 let mut mutr = doc.mutate();
106 let html_element_id = mutr.create_element(qual_name("html", None), vec![]);
107 mutr.append_children(mutr.doc.root_node().id, &[html_element_id]);
108
109 let head_element_id = mutr.create_element(qual_name("head", None), vec![]);
111 mutr.append_children(html_element_id, &[head_element_id]);
112
113 let body_element_id = mutr.create_element(qual_name("body", None), vec![]);
115 mutr.append_children(html_element_id, &[body_element_id]);
116
117 let main_attr = blitz_dom::Attribute {
119 name: qual_name("id", None),
120 value: "main".to_string(),
121 };
122 let main_element_id = mutr.create_element(qual_name("main", None), vec![main_attr]);
123 mutr.append_children(body_element_id, &[main_element_id]);
124
125 drop(mutr);
126
127 let vdom_state = DioxusState::create(main_element_id);
128 Self {
129 vdom,
130 vdom_state,
131 inner: doc,
132 html_element_id,
133 head_element_id,
134 body_element_id,
135 main_element_id,
136 }
137 }
138
139 pub fn initial_build(&mut self) {
141 let mut writer = MutationWriter::new(&mut self.inner, &mut self.vdom_state);
142 self.vdom.rebuild(&mut writer);
143 }
144
145 #[doc(hidden)]
148 pub fn create_head_element(
149 &mut self,
150 name: &str,
151 attributes: &[(String, String)],
152 contents: &Option<String>,
153 ) {
154 let mut mutr = self.inner.mutate();
155
156 let attributes = attributes
157 .iter()
158 .map(|(name, value)| Attribute {
159 name: qual_name(name, None),
160 value: value.clone(),
161 })
162 .collect();
163
164 let new_elem_id = mutr.create_element(qual_name(name, None), attributes);
165 mutr.append_children(self.head_element_id, &[new_elem_id]);
166 if let Some(contents) = contents {
167 let text_node_id = mutr.create_text_node(contents);
168 mutr.append_children(new_elem_id, &[text_node_id]);
169 }
170 }
171}
172
173impl Document for DioxusDocument {
175 fn id(&self) -> usize {
176 self.inner.id()
177 }
178
179 fn as_any_mut(&mut self) -> &mut dyn Any {
180 self
181 }
182
183 fn poll(&mut self, cx: Option<TaskContext>) -> bool {
184 {
185 let fut = self.vdom.wait_for_work();
186 pin_mut!(fut);
187
188 static NOOP_WAKER: LazyLock<Waker> = LazyLock::new(noop_waker);
189 let mut cx = cx.unwrap_or_else(|| TaskContext::from_waker(&NOOP_WAKER));
190 match fut.poll_unpin(&mut cx) {
191 std::task::Poll::Ready(_) => {}
192 std::task::Poll::Pending => return false,
193 }
194 }
195
196 let mut writer = MutationWriter::new(&mut self.inner, &mut self.vdom_state);
197 self.vdom.render_immediate(&mut writer);
198
199 true
200 }
201
202 fn handle_ui_event(&mut self, event: UiEvent) {
203 let handler = DioxusEventHandler {
204 vdom: &mut self.vdom,
205 vdom_state: &mut self.vdom_state,
206 };
207 let mut driver = EventDriver::new(self.inner.mutate(), handler);
208 driver.handle_ui_event(event);
209 }
210}
211
212impl Deref for DioxusDocument {
213 type Target = BaseDocument;
214 fn deref(&self) -> &BaseDocument {
215 &self.inner
216 }
217}
218impl DerefMut for DioxusDocument {
219 fn deref_mut(&mut self) -> &mut Self::Target {
220 &mut self.inner
221 }
222}
223impl From<DioxusDocument> for BaseDocument {
224 fn from(doc: DioxusDocument) -> BaseDocument {
225 doc.inner
226 }
227}
228
229pub struct DioxusEventHandler<'v> {
230 vdom: &'v mut VirtualDom,
231 #[allow(dead_code, reason = "WIP")]
232 vdom_state: &'v mut DioxusState,
233}
234
235impl EventHandler for DioxusEventHandler<'_> {
236 fn handle_event(
237 &mut self,
238 chain: &[usize],
239 event: &mut DomEvent,
240 mutr: &mut blitz_dom::DocumentMutator<'_>,
241 event_state: &mut EventState,
242 ) {
243 let event_kind_idx = event.data.discriminant() as usize;
246 let event_kind_count = self.vdom_state.event_handler_counts[event_kind_idx];
247 if event_kind_count == 0 {
248 return;
249 }
250
251 let event_data = match &event.data {
252 DomEventData::MouseMove(mevent)
253 | DomEventData::MouseDown(mevent)
254 | DomEventData::MouseUp(mevent)
255 | DomEventData::Click(mevent) => Some(wrap_event_data(NativeClickData(mevent.clone()))),
256
257 DomEventData::KeyDown(kevent)
258 | DomEventData::KeyUp(kevent)
259 | DomEventData::KeyPress(kevent) => {
260 Some(wrap_event_data(BlitzKeyboardData(kevent.clone())))
261 }
262
263 DomEventData::Input(data) => Some(wrap_event_data(NativeFormData {
264 value: data.value.clone(),
265 values: vec![],
266 })),
267
268 DomEventData::Ime(_) => None,
270 };
271
272 let Some(event_data) = event_data else {
273 return;
274 };
275
276 for &node_id in chain {
277 let dioxus_id = mutr.doc.get_node(node_id).and_then(get_dioxus_id);
279 let Some(id) = dioxus_id else {
280 continue;
281 };
282
283 let dx_event = Event::new(event_data.clone(), event.bubbles);
285 self.vdom
286 .runtime()
287 .handle_event(event.name(), dx_event.clone(), id);
288
289 if !dx_event.default_action_enabled() {
291 event_state.prevent_default();
292 }
293 if !dx_event.propagates() {
294 event_state.stop_propagation();
295 break;
296 }
297 }
298 }
299}