1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
mod base;

use std::{any::Any, fmt::Debug, rc::Rc};

use base::{AsRespoEffectBase, RespoEffectDynEq};
use cirru_parser::Cirru;
use web_sys::Node;

/// trait for component effects
/// you can declare `mounted`, `beforeUpdate`, `updated`, `beforeUnmount` methods
/// to handle lifecycle events, mainly for manually manipulating DOM
pub trait RespoEffect
where
  Self: Debug + Any + RespoEffectDynEq + AsRespoEffectBase + 'static,
{
  /// actually run effect
  #[allow(unused_variables)]
  fn run(&self, effect_type: RespoEffectType, el: &Node) -> Result<(), String> {
    match effect_type {
      RespoEffectType::Mounted => self.mounted(el),
      RespoEffectType::BeforeUpdate => self.before_update(el),
      RespoEffectType::Updated => self.updated(el),
      RespoEffectType::BeforeUnmount => self.before_unmount(el),
    }
  }
  /// called when mounted
  #[allow(unused_variables)]
  fn mounted(&self, el: &Node) -> Result<(), String> {
    Ok(())
  }
  /// called when before update
  #[allow(unused_variables)]
  fn before_update(&self, el: &Node) -> Result<(), String> {
    Ok(())
  }
  /// called when updated
  #[allow(unused_variables)]
  fn updated(&self, el: &Node) -> Result<(), String> {
    Ok(())
  }
  /// called when before unmount
  #[allow(unused_variables)]
  fn before_unmount(&self, el: &Node) -> Result<(), String> {
    Ok(())
  }
}

/// wraps dyn trait object of effect
#[derive(Debug, Clone)]
pub struct RespoEffectBox(pub Rc<dyn RespoEffect>);

impl PartialEq for RespoEffectBox {
  fn eq(&self, other: &Self) -> bool {
    let r = self.0.as_ref();
    r.do_eq(other.0.as_ref().as_base()) == Some(true)
  }
}
impl Eq for RespoEffectBox {}

impl RespoEffectBox {
  pub fn new<T>(v: T) -> Self
  where
    T: RespoEffect + 'static,
  {
    Self(Rc::new(v))
  }
}

// use crate::{log, util::print_type_of};

/// Internal enum for effect types.
/// you only need this if you override `RespoEffect` `.run()`.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum RespoEffectType {
  /// called after mounting happened, use effect handlers from new trees
  Mounted,
  /// called before effect arguments changed, use effect hanles from new trees
  BeforeUpdate,
  /// called after effect arguments changed, use effect handles from new trees
  Updated,
  /// called before unmounting, use effect handles from **old** trees
  BeforeUnmount,
}

impl From<RespoEffectType> for Cirru {
  fn from(effect_type: RespoEffectType) -> Self {
    match effect_type {
      RespoEffectType::Mounted => "::mounted".into(),
      RespoEffectType::BeforeUpdate => "::before-update".into(),
      RespoEffectType::Updated => "::updated".into(),
      RespoEffectType::BeforeUnmount => "::before-unmount".into(),
    }
  }
}