1use maomi::{
4 backend::tree::*,
5 locale_string::LocaleString,
6 prop::{BindingValue, PropertyUpdate},
7};
8use std::{
9 borrow::Borrow,
10 cell::RefCell,
11 mem::{ManuallyDrop, MaybeUninit},
12 num::NonZeroUsize,
13 ops::Deref,
14 rc::Rc,
15};
16use wasm_bindgen::{prelude::*, JsCast};
17
18use crate::{
19 event::{ColdEventItem, ColdEventList, HotEventList},
20 DomGeneralElement, DomState, WriteHtmlState,
21};
22
23#[wasm_bindgen]
24extern "C" {
25 type MaomiDomElement;
26 #[wasm_bindgen(method, getter)]
27 fn maomi(this: &MaomiDomElement) -> Option<usize>;
28 #[wasm_bindgen(method, setter)]
29 fn set_maomi(this: &MaomiDomElement, ptr: usize);
30}
31
32#[cfg(feature = "prerendering")]
33#[derive(Debug, Clone)]
34pub(crate) struct PrerenderingElement {
35 tag_name: &'static str,
36 classes: Vec<&'static str>,
37 styles: Vec<(&'static str, String)>,
38 attrs: Vec<(&'static str, String)>,
39}
40
41#[cfg(feature = "prerendering")]
42impl PrerenderingElement {
43 pub(crate) fn new(tag_name: &'static str) -> Self {
44 Self {
45 tag_name,
46 classes: vec![],
47 styles: vec![],
48 attrs: vec![],
49 }
50 }
51
52 pub(crate) fn set_attribute(&mut self, name: &'static str, value: String) {
53 if let Some((_, v)) = self.attrs.iter_mut().find(|(n, _)| *n == name) {
54 *v = value;
55 } else {
56 self.attrs.push((name, value));
57 }
58 }
59
60 pub(crate) fn remove_attribute(&mut self, name: &'static str) {
61 if let Some(index) = self.attrs.iter_mut().position(|(n, _)| *n == name) {
62 self.attrs.swap_remove(index);
63 }
64 }
65
66 pub(crate) fn add_class(&mut self, class_name: &'static str) {
67 if !self.classes.contains(&class_name) {
68 self.classes.push(class_name);
69 }
70 }
71
72 pub(crate) fn remove_class(&mut self, class_name: &'static str) {
73 if let Some(index) = self.classes.iter().position(|x| *x == class_name) {
74 self.classes.swap_remove(index);
75 }
76 }
77
78 pub(crate) fn set_style(&mut self, style_name: &'static str, value: &str) {
79 let need_push = self
80 .styles
81 .iter_mut()
82 .find_map(|(n, v)| {
83 if *n == style_name {
84 *v = value.to_string();
85 return Some(());
86 }
87 None
88 })
89 .is_none();
90 if need_push {
91 self.styles.push((style_name, value.to_string()));
92 }
93 }
94
95 #[cfg(feature = "prerendering")]
96 pub(crate) fn write_children_html(
97 &self,
98 w: &mut impl std::io::Write,
99 this: &ForestNode<DomGeneralElement>,
100 state: &mut WriteHtmlState,
101 ) -> std::io::Result<()> {
102 let mut cur = this.first_child();
103 while let Some(c) = &cur {
104 DomGeneralElement::write_outer_html(&c, w, state)?;
105 cur = c.next_sibling();
106 }
107 Ok(())
108 }
109
110 #[cfg(feature = "prerendering")]
111 pub(crate) fn write_html(
112 &self,
113 w: &mut impl std::io::Write,
114 this: &ForestNode<DomGeneralElement>,
115 state: &mut WriteHtmlState,
116 ) -> std::io::Result<()> {
117 write!(w, "<{}", self.tag_name)?;
118 let mut has_class = false;
119 for c in &self.classes {
120 if c.len() == 0 {
121 continue;
122 };
123 if !has_class {
124 has_class = true;
125 write!(w, r#" class=""#)?;
126 } else {
127 write!(w, " ")?;
128 }
129 write!(w, "{}", c)?;
130 }
131 if has_class {
132 write!(w, r#"""#)?;
133 }
134 let mut has_style = false;
135 for (name, value) in &self.styles {
136 if !has_style {
137 has_style = true;
138 write!(w, r#" style=""#)?;
139 } else {
140 write!(w, ";")?;
141 }
142 write!(w, "{}:", name)?;
143 html_escape::encode_double_quoted_attribute_to_writer(&value, w)?;
144 }
145 if has_style {
146 write!(w, r#"""#)?;
147 }
148 for (name, value) in &self.attrs {
149 write!(w, r#" {}=""#, name)?;
150 html_escape::encode_double_quoted_attribute_to_writer(&value, w)?;
151 write!(w, r#"""#)?;
152 }
153 write!(w, ">")?;
154 state.prev_is_text_node = false;
155 self.write_children_html(w, this, state)?;
156 write!(w, "</{}>", self.tag_name)?;
157 state.prev_is_text_node = false;
158 Ok(())
159 }
160}
161
162#[cfg(feature = "prerendering-apply")]
163#[derive(Clone)]
164pub(crate) struct RematchedDomElem {
165 inner: std::rc::Rc<std::cell::Cell<Option<web_sys::Element>>>,
166}
167
168#[cfg(feature = "prerendering-apply")]
169impl RematchedDomElem {
170 pub(crate) fn new() -> Self {
171 Self {
172 inner: Default::default(),
173 }
174 }
175
176 pub(crate) fn set(&mut self, e: web_sys::Element) {
177 self.inner.set(Some(e));
178 }
179
180 pub(crate) fn take(&self) -> Option<web_sys::Element> {
181 self.inner.take()
182 }
183}
184
185#[doc(hidden)]
186pub struct DomElement {
187 pub(crate) elem: dom_state_ty!(web_sys::Element, PrerenderingElement, RematchedDomElem),
188 pub(crate) forest_token: ManuallyDrop<ForestToken>,
189 hot_event_list: Option<Box<HotEventList>>,
190 cold_event_list: Option<Box<ColdEventList>>,
191}
192
193impl Drop for DomElement {
194 #[inline]
195 fn drop(&mut self) {
196 if self.hot_event_list.is_some() || self.cold_event_list.is_some() {
197 match &self.elem {
198 DomState::Normal(x) => {
199 x.unchecked_ref::<MaomiDomElement>().set_maomi(0);
200 }
201 #[cfg(feature = "prerendering")]
202 DomState::Prerendering(_) => {}
203 #[cfg(feature = "prerendering-apply")]
204 DomState::PrerenderingApply(_) => {}
205 }
206 crate::event::tap::remove_element_touch_state(&self.forest_token);
207 }
208 unsafe {
209 ManuallyDrop::drop(&mut self.forest_token);
210 }
211 }
212}
213
214impl std::fmt::Debug for DomElement {
215 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
216 match &self.elem {
217 DomState::Normal(x) => write!(f, "<{}>", x.tag_name()),
218 #[cfg(feature = "prerendering")]
219 DomState::Prerendering(_) => write!(f, "<(prerendering)>"),
220 #[cfg(feature = "prerendering-apply")]
221 DomState::PrerenderingApply(_) => write!(f, "<(prerendering)>"),
222 }
223 }
224}
225
226impl DomElement {
227 pub(crate) unsafe fn new(
229 elem: dom_state_ty!(web_sys::Element, PrerenderingElement, RematchedDomElem),
230 ) -> Self {
231 let x = MaybeUninit::uninit();
232 Self {
233 elem,
234 forest_token: ManuallyDrop::new(x.assume_init()),
235 hot_event_list: None,
236 cold_event_list: None,
237 }
238 }
239
240 pub(crate) fn init(&mut self, forest_token: ForestToken) {
241 self.forest_token = ManuallyDrop::new(forest_token);
242 }
243
244 pub(crate) fn is_prerendering(&self) -> dom_state_ty!((), (), ()) {
245 match &self.elem {
246 DomState::Normal(_) => DomState::Normal(()),
247 #[cfg(feature = "prerendering")]
248 DomState::Prerendering(_) => DomState::Prerendering(()),
249 #[cfg(feature = "prerendering-apply")]
250 DomState::PrerenderingApply(_) => DomState::PrerenderingApply(()),
251 }
252 }
253
254 pub(crate) fn composing_dom(&self) -> &web_sys::Node {
255 match &self.elem {
256 DomState::Normal(x) => &x,
257 #[cfg(feature = "prerendering")]
258 DomState::Prerendering(_) => unreachable!(),
259 #[cfg(feature = "prerendering-apply")]
260 DomState::PrerenderingApply(_) => unreachable!(),
261 }
262 }
263
264 #[cfg(feature = "prerendering-apply")]
265 pub(crate) fn rematch_dom(&mut self, e: web_sys::Node) {
266 if let DomState::PrerenderingApply(x) = &mut self.elem {
267 x.set(e.clone().unchecked_into());
268 }
269 for item in self.cold_event_list_mut() {
270 item.apply(e.unchecked_ref());
271 }
272 self.elem = DomState::Normal(e.unchecked_into());
273 if self.hot_event_list.is_some() || self.cold_event_list.is_some() {
274 self.init_event_token();
275 }
276 }
277
278 pub(crate) fn write_inner_html(
279 &self,
280 _this: &ForestNode<DomGeneralElement>,
281 w: &mut impl std::io::Write,
282 _state: &mut WriteHtmlState,
283 ) -> std::io::Result<()> {
284 match &self.elem {
285 DomState::Normal(x) => {
286 write!(w, "{}", x.inner_html())?;
287 }
288 #[cfg(feature = "prerendering")]
289 DomState::Prerendering(x) => {
290 x.write_children_html(w, _this, _state).unwrap();
291 }
292 #[cfg(feature = "prerendering-apply")]
293 DomState::PrerenderingApply(_) => {}
294 }
295 Ok(())
296 }
297
298 pub(crate) fn write_outer_html(
299 &self,
300 _this: &ForestNode<DomGeneralElement>,
301 w: &mut impl std::io::Write,
302 _state: &mut WriteHtmlState,
303 ) -> std::io::Result<()> {
304 match &self.elem {
305 DomState::Normal(x) => {
306 write!(w, "{}", x.outer_html())?;
307 }
308 #[cfg(feature = "prerendering")]
309 DomState::Prerendering(x) => {
310 x.write_html(w, _this, _state).unwrap();
311 }
312 #[cfg(feature = "prerendering-apply")]
313 DomState::PrerenderingApply(_) => {}
314 }
315 Ok(())
316 }
317
318 pub(crate) fn from_event_dom_elem(
319 dom_elem: &web_sys::Element,
320 bubbles: bool,
321 ) -> Option<ForestNodeRc<DomGeneralElement>> {
322 let ptr = dom_elem
323 .unchecked_ref::<MaomiDomElement>()
324 .maomi()
325 .and_then(|x| NonZeroUsize::new(x));
326 if let Some(ptr) = ptr {
327 return unsafe {
328 ForestTokenAddr::from_ptr(ptr.get() as *const ())
329 .token()
330 .unsafe_resolve_token()
331 };
332 }
333 if !bubbles {
334 return None;
335 }
336 let mut next = dom_elem.parent_element();
337 while let Some(cur) = next.as_ref() {
338 let ptr = cur
339 .unchecked_ref::<MaomiDomElement>()
340 .maomi()
341 .and_then(|x| NonZeroUsize::new(x));
342 if let Some(ptr) = ptr {
343 return unsafe {
344 ForestTokenAddr::from_ptr(ptr.get() as *const ())
345 .token()
346 .unsafe_resolve_token()
347 };
348 }
349 next = cur.parent_element();
350 }
351 None
352 }
353
354 fn init_event_token(&mut self) {
355 let ptr = self.forest_token.stable_addr().ptr() as usize;
356 match &self.elem {
357 DomState::Normal(x) => {
358 x.unchecked_ref::<MaomiDomElement>().set_maomi(ptr as usize);
359 }
360 #[cfg(feature = "prerendering")]
361 DomState::Prerendering(_) => {}
362 #[cfg(feature = "prerendering-apply")]
363 DomState::PrerenderingApply(_) => {}
364 }
365 }
366
367 pub(crate) fn hot_event_list_mut(&mut self) -> &mut HotEventList {
368 if self.hot_event_list.is_none() {
369 self.hot_event_list = Some(Default::default());
370 if self.cold_event_list.is_none() {
371 self.init_event_token();
372 }
373 }
374 self.hot_event_list.as_mut().unwrap()
375 }
376
377 pub(crate) fn hot_event_list(&self) -> Option<&HotEventList> {
378 self.hot_event_list.as_ref().map(|x| &**x)
379 }
380
381 pub(crate) fn cold_event_list_mut(&mut self) -> &mut ColdEventList {
382 if self.cold_event_list.is_none() {
383 self.cold_event_list = Some(Default::default());
384 if self.hot_event_list.is_none() {
385 self.init_event_token();
386 }
387 }
388 self.cold_event_list.as_mut().unwrap()
389 }
390
391 pub(crate) fn cold_event_list(&self) -> Option<&ColdEventList> {
392 self.cold_event_list.as_ref().map(|x| &**x)
393 }
394}
395
396pub(crate) trait DomElementBase {
397 fn dom_element_lazy(
398 &self,
399 ) -> &std::cell::UnsafeCell<dom_state_ty!(web_sys::Element, (), RematchedDomElem)>;
400}
401
402pub trait DomElementExt {
404 fn dom_element(&self) -> &web_sys::Element;
406}
407
408impl<T: DomElementBase> DomElementExt for T {
409 #[inline]
410 fn dom_element(&self) -> &web_sys::Element {
411 let ptr = self.dom_element_lazy().get();
412 #[cfg(feature = "prerendering-apply")]
413 if let DomState::PrerenderingApply(x) = unsafe { &*ptr } {
414 if let Some(e) = x.take() {
417 unsafe {
418 *ptr = DomState::Normal(e);
419 }
420 }
421 }
422 match unsafe { &*ptr } {
423 DomState::Normal(x) => x,
424 #[cfg(feature = "prerendering")]
425 DomState::Prerendering(_) => {
426 panic!("Cannot get DOM element in prerendering stage")
427 }
428 #[cfg(feature = "prerendering-apply")]
429 DomState::PrerenderingApply(_) => {
430 panic!("Cannot get DOM element in prerendering-apply stage")
431 }
432 }
433 }
434}
435
436pub struct DomStrAttr {
438 pub(crate) inner: String,
439 pub(crate) f: fn(&web_sys::HtmlElement, &str),
440 #[cfg(feature = "prerendering")]
441 pub(crate) attr_name: &'static str,
442}
443
444impl Deref for DomStrAttr {
445 type Target = String;
446
447 #[inline]
448 fn deref(&self) -> &Self::Target {
449 &self.inner
450 }
451}
452
453impl<S: ?Sized + PartialEq + ToOwned<Owned = String>> PropertyUpdate<S> for DomStrAttr
454where
455 String: Borrow<S>,
456{
457 type UpdateContext = DomElement;
458
459 #[inline]
460 fn compare_and_set_ref(dest: &mut Self, src: &S, ctx: &mut DomElement) {
461 if dest.inner.borrow() == src {
462 return;
463 }
464 dest.inner = src.to_owned();
465 match &mut ctx.elem {
466 DomState::Normal(x) => {
467 (dest.f)(x.unchecked_ref(), &dest.inner);
468 }
469 #[cfg(feature = "prerendering")]
470 DomState::Prerendering(x) => {
471 x.set_attribute(dest.attr_name, dest.inner.clone());
472 }
473 #[cfg(feature = "prerendering-apply")]
474 DomState::PrerenderingApply(_) => {}
475 }
476 }
477}
478
479pub struct DomLocaleStringAttr {
481 pub(crate) inner: LocaleString,
482 pub(crate) f: fn(&web_sys::HtmlElement, &LocaleString),
483 #[cfg(feature = "prerendering")]
484 pub(crate) attr_name: &'static str,
485}
486
487impl Deref for DomLocaleStringAttr {
488 type Target = LocaleString;
489
490 #[inline]
491 fn deref(&self) -> &Self::Target {
492 &self.inner
493 }
494}
495
496impl<S: ?Sized + PartialEq + ToOwned<Owned = LocaleString>> PropertyUpdate<S>
497 for DomLocaleStringAttr
498where
499 LocaleString: Borrow<S>,
500{
501 type UpdateContext = DomElement;
502
503 #[inline]
504 fn compare_and_set_ref(dest: &mut Self, src: &S, ctx: &mut DomElement) {
505 if dest.inner.borrow() == src {
506 return;
507 }
508 dest.inner = src.to_owned();
509 match &mut ctx.elem {
510 DomState::Normal(x) => {
511 (dest.f)(x.unchecked_ref(), &dest.inner);
512 }
513 #[cfg(feature = "prerendering")]
514 DomState::Prerendering(x) => {
515 x.set_attribute(dest.attr_name, dest.inner.to_string());
516 }
517 #[cfg(feature = "prerendering-apply")]
518 DomState::PrerenderingApply(_) => {}
519 }
520 }
521}
522
523pub struct DomBoolAttr {
528 pub(crate) inner: bool,
529 pub(crate) f: fn(&web_sys::HtmlElement, bool),
530 #[cfg(feature = "prerendering")]
531 pub(crate) attr_name: &'static str,
532}
533
534impl Deref for DomBoolAttr {
535 type Target = bool;
536
537 #[inline]
538 fn deref(&self) -> &Self::Target {
539 &self.inner
540 }
541}
542
543impl<S: ?Sized + PartialEq + ToOwned<Owned = bool>> PropertyUpdate<S> for DomBoolAttr
544where
545 bool: Borrow<S>,
546{
547 type UpdateContext = DomElement;
548
549 #[inline]
550 fn compare_and_set_ref(dest: &mut Self, src: &S, ctx: &mut DomElement) {
551 if dest.inner.borrow() == src {
552 return;
553 }
554 dest.inner = src.to_owned();
555 match &mut ctx.elem {
556 DomState::Normal(x) => {
557 (dest.f)(x.unchecked_ref(), dest.inner);
558 }
559 #[cfg(feature = "prerendering")]
560 DomState::Prerendering(x) => {
561 if dest.inner {
562 x.set_attribute(dest.attr_name, String::with_capacity(0));
563 } else {
564 x.remove_attribute(dest.attr_name);
565 }
566 }
567 #[cfg(feature = "prerendering-apply")]
568 DomState::PrerenderingApply(_) => {}
569 }
570 }
571}
572
573pub struct DomU32Attr {
575 pub(crate) inner: u32,
576 pub(crate) f: fn(&web_sys::HtmlElement, u32),
577 #[cfg(feature = "prerendering")]
578 pub(crate) attr_name: &'static str,
579}
580
581impl Deref for DomU32Attr {
582 type Target = u32;
583
584 #[inline]
585 fn deref(&self) -> &Self::Target {
586 &self.inner
587 }
588}
589
590impl<S: ?Sized + PartialEq + ToOwned<Owned = u32>> PropertyUpdate<S> for DomU32Attr
591where
592 u32: Borrow<S>,
593{
594 type UpdateContext = DomElement;
595
596 #[inline]
597 fn compare_and_set_ref(dest: &mut Self, src: &S, ctx: &mut DomElement) {
598 if dest.inner.borrow() == src {
599 return;
600 }
601 dest.inner = src.to_owned();
602 match &mut ctx.elem {
603 DomState::Normal(x) => {
604 (dest.f)(x.unchecked_ref(), dest.inner);
605 }
606 #[cfg(feature = "prerendering")]
607 DomState::Prerendering(x) => {
608 x.set_attribute(dest.attr_name, dest.inner.to_string());
609 }
610 #[cfg(feature = "prerendering-apply")]
611 DomState::PrerenderingApply(_) => {}
612 }
613 }
614}
615
616pub struct DomI32Attr {
618 pub(crate) inner: i32,
619 pub(crate) f: fn(&web_sys::HtmlElement, i32),
620 #[cfg(feature = "prerendering")]
621 pub(crate) attr_name: &'static str,
622}
623
624impl Deref for DomI32Attr {
625 type Target = i32;
626
627 #[inline]
628 fn deref(&self) -> &Self::Target {
629 &self.inner
630 }
631}
632
633impl<S: ?Sized + PartialEq + ToOwned<Owned = i32>> PropertyUpdate<S> for DomI32Attr
634where
635 i32: Borrow<S>,
636{
637 type UpdateContext = DomElement;
638
639 #[inline]
640 fn compare_and_set_ref(dest: &mut Self, src: &S, ctx: &mut DomElement) {
641 if dest.inner.borrow() == src {
642 return;
643 }
644 dest.inner = src.to_owned();
645 match &mut ctx.elem {
646 DomState::Normal(x) => {
647 (dest.f)(x.unchecked_ref(), dest.inner);
648 }
649 #[cfg(feature = "prerendering")]
650 DomState::Prerendering(x) => {
651 x.set_attribute(dest.attr_name, dest.inner.to_string());
652 }
653 #[cfg(feature = "prerendering-apply")]
654 DomState::PrerenderingApply(_) => {}
655 }
656 }
657}
658
659pub struct DomF64Attr {
661 pub(crate) inner: f64,
662 pub(crate) f: fn(&web_sys::HtmlElement, f64),
663 #[cfg(feature = "prerendering")]
664 pub(crate) attr_name: &'static str,
665}
666
667impl Deref for DomF64Attr {
668 type Target = f64;
669
670 #[inline]
671 fn deref(&self) -> &Self::Target {
672 &self.inner
673 }
674}
675
676impl<S: ?Sized + PartialEq + ToOwned<Owned = f64>> PropertyUpdate<S> for DomF64Attr
677where
678 f64: Borrow<S>,
679{
680 type UpdateContext = DomElement;
681
682 #[inline]
683 fn compare_and_set_ref(dest: &mut Self, src: &S, ctx: &mut DomElement) {
684 if dest.inner.borrow() == src {
685 return;
686 }
687 dest.inner = src.to_owned();
688 match &mut ctx.elem {
689 DomState::Normal(x) => {
690 (dest.f)(x.unchecked_ref(), dest.inner);
691 }
692 #[cfg(feature = "prerendering")]
693 DomState::Prerendering(x) => {
694 x.set_attribute(dest.attr_name, dest.inner.to_string());
695 }
696 #[cfg(feature = "prerendering-apply")]
697 DomState::PrerenderingApply(_) => {}
698 }
699 }
700}
701
702pub struct DomBindingStrAttr {
706 pub(crate) inner: Rc<RefCell<BindingValue<String>>>,
707 pub(crate) f: fn(&web_sys::HtmlElement, &str),
708 #[cfg(feature = "prerendering")]
709 pub(crate) attr_name: &'static str,
710}
711
712impl DomBindingStrAttr {
713 #[inline]
715 pub fn with<R>(&self, f: impl FnOnce(&String) -> R) -> R {
716 (*self.inner).borrow().with(f)
717 }
718
719 #[inline]
721 pub fn get(&self) -> String {
722 (*self.inner).borrow().get()
723 }
724}
725
726impl PropertyUpdate<BindingValue<String>> for DomBindingStrAttr {
727 type UpdateContext = DomElement;
728
729 #[inline]
730 fn compare_and_set_ref(
731 dest: &mut Self,
732 src: &BindingValue<String>,
733 ctx: &mut Self::UpdateContext,
734 ) {
735 let inner = &mut dest.inner.borrow_mut();
736 if BindingValue::ptr_eq(inner, src) {
737 return;
738 }
739 let old = std::mem::replace(&mut **inner, src.clone_ref());
740 inner.with(|inner| {
741 if old.with(|x| inner == x) {
742 return;
743 }
744 match &mut ctx.elem {
745 DomState::Normal(x) => {
746 (dest.f)(x.unchecked_ref(), inner);
747 }
748 #[cfg(feature = "prerendering")]
749 DomState::Prerendering(x) => {
750 x.set_attribute(dest.attr_name, inner.to_string());
751 }
752 #[cfg(feature = "prerendering-apply")]
753 DomState::PrerenderingApply(_) => {}
754 }
755 });
756 }
757}
758
759pub struct DomBindingBoolAttr {
763 pub(crate) inner: Rc<RefCell<BindingValue<bool>>>,
764 pub(crate) f: fn(&web_sys::HtmlElement, bool),
765 #[cfg(feature = "prerendering")]
766 pub(crate) attr_name: &'static str,
767}
768
769impl DomBindingBoolAttr {
770 #[inline]
772 pub fn with<R>(&self, f: impl FnOnce(&bool) -> R) -> R {
773 (*self.inner).borrow().with(f)
774 }
775
776 #[inline]
778 pub fn get(&self) -> bool {
779 (*self.inner).borrow().get()
780 }
781}
782
783impl PropertyUpdate<BindingValue<bool>> for DomBindingBoolAttr {
784 type UpdateContext = DomElement;
785
786 #[inline]
787 fn compare_and_set_ref(
788 dest: &mut Self,
789 src: &BindingValue<bool>,
790 ctx: &mut Self::UpdateContext,
791 ) {
792 let inner = &mut dest.inner.borrow_mut();
793 if BindingValue::ptr_eq(inner, src) {
794 return;
795 }
796 let old = std::mem::replace(&mut **inner, src.clone_ref());
797 inner.with(|inner| {
798 if old.with(|x| inner == x) {
799 return;
800 }
801 match &mut ctx.elem {
802 DomState::Normal(x) => {
803 (dest.f)(x.unchecked_ref(), *inner);
804 }
805 #[cfg(feature = "prerendering")]
806 DomState::Prerendering(x) => {
807 if *inner {
808 x.set_attribute(dest.attr_name, String::with_capacity(0));
809 } else {
810 x.remove_attribute(dest.attr_name);
811 }
812 }
813 #[cfg(feature = "prerendering-apply")]
814 DomState::PrerenderingApply(_) => {}
815 }
816 });
817 }
818}
819
820pub struct DomBindingF64Attr {
824 pub(crate) inner: Rc<RefCell<BindingValue<f64>>>,
825 pub(crate) f: fn(&web_sys::HtmlElement, f64),
826 #[cfg(feature = "prerendering")]
827 pub(crate) attr_name: &'static str,
828}
829
830impl DomBindingF64Attr {
831 #[inline]
833 pub fn with<R>(&self, f: impl FnOnce(&f64) -> R) -> R {
834 (*self.inner).borrow().with(f)
835 }
836
837 #[inline]
839 pub fn get(&self) -> f64 {
840 (*self.inner).borrow().get()
841 }
842}
843
844impl PropertyUpdate<BindingValue<f64>> for DomBindingF64Attr {
845 type UpdateContext = DomElement;
846
847 #[inline]
848 fn compare_and_set_ref(
849 dest: &mut Self,
850 src: &BindingValue<f64>,
851 ctx: &mut Self::UpdateContext,
852 ) {
853 let inner = &mut dest.inner.borrow_mut();
854 if BindingValue::ptr_eq(inner, src) {
855 return;
856 }
857 let old = std::mem::replace(&mut **inner, src.clone_ref());
858 inner.with(|inner| {
859 if old.with(|x| inner == x) {
860 return;
861 }
862 match &mut ctx.elem {
863 DomState::Normal(x) => {
864 (dest.f)(x.unchecked_ref(), *inner);
865 }
866 #[cfg(feature = "prerendering")]
867 DomState::Prerendering(x) => {
868 x.set_attribute(dest.attr_name, inner.to_string());
869 }
870 #[cfg(feature = "prerendering-apply")]
871 DomState::PrerenderingApply(_) => {}
872 }
873 });
874 }
875}
876
877pub(crate) fn init_binding_prop(
878 target: &mut DomElement,
879 name: &'static str,
880 f: impl 'static + Fn(web_sys::Event),
881) {
882 #[cfg(feature = "prerendering")]
883 if let crate::DomState::Prerendering(_) = &target.elem {
884 return;
885 }
886 let c = Closure::new(move |e| f(e));
887 let item = ColdEventItem::BindingEventListener(name, c);
888 match &target.elem {
889 crate::DomState::Normal(x) => item.apply(x),
890 #[cfg(feature = "prerendering")]
891 crate::DomState::Prerendering(_) => unreachable!(),
892 #[cfg(feature = "prerendering-apply")]
893 crate::DomState::PrerenderingApply(_) => {}
894 }
895 target.cold_event_list_mut().push(item);
896}