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
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
//! Traits for constructing declarative views.
pub use web_sys::{Element, Event, EventTarget, HtmlElement};

use crate::prelude::{Effect, Receiver, Transmitter};

/// `ElementView`s are views that represent DOM elements.
pub trait ElementView {
    /// Create a view with the given element tag.
    fn element(tag: &str) -> Self;

    /// Create a view with the given element tag and namespace.
    fn element_ns(tag: &str, ns: &str) -> Self;
}

/// `TextView`s are views that represent text nodes.
pub trait TextView {
    /// Create a new text node view.
    fn text<E: Into<Effect<String>>>(eff: E) -> Self;
}

/// `AttributeView`s can describe themselves with key value pairs.
pub trait AttributeView {
    /// Create a named attribute on the view that may change over time as
    /// a receiver receives a message.
    /// Here's an example that builds a gizmo with inital id and class values,
    /// and updates the class value whenever a message is received on a Receiver:
    ///
    /// ```rust, no_run
    /// extern crate mogwai;
    /// use mogwai::prelude::*;
    ///
    /// let (tx, rx) = txrx::<String>();
    /// let mut my_div: View<HtmlElement> = View::element("div");
    /// my_div.attribute("id", "my_div");
    /// my_div.attribute("class", ("hero_div", rx.branch_map(|class_update| {
    ///     ["hero_div", class_update].join(" ")
    /// })));
    /// ```
    ///
    /// Alternatively you can use macros to define an equivalent view in RSX:
    ///
    /// ```rust, no_run
    /// extern crate mogwai;
    /// use mogwai::prelude::*;
    ///
    /// let (tx, rx) = txrx::<String>();
    /// let my_div:View<HtmlElement> = view! {
    ///     <div id="my_div" class=("hero_div", rx.branch_map(|class_update| {
    ///         ["hero_div", class_update].join(" ")
    ///     })) />
    /// };
    /// ```
    fn attribute<E: Into<Effect<String>>>(&mut self, name: &str, eff: E);

    /// Create (or remove) a boolean attribute on the view that may change its
    /// value every time the given receiver receives a message
    /// If `eff` contains a receiver and that receiver receives `false` it will
    /// respond by removing the attribute until it receives `true`. If `eff` is
    /// a single boolean value, either add or remove the attribute.
    fn boolean_attribute<E: Into<Effect<bool>>>(&mut self, name: &str, eff: E);
}

/// `StyleView`s can describe themselves using CSS style key value pairs.
pub trait StyleView {
    /// Set a CSS property in the style attribute of the view being built.
    /// If `eff` contains a Receiver, messages received will updated the style's
    /// value.
    fn style<E: Into<Effect<String>>>(&mut self, name: &str, eff: E);
}

/// `EventTargetView`s can transmit messages when events occur within them. They can
/// also transmit messages when events occur within the window or the document.
pub trait EventTargetView {
    /// Transmit an event message on the given transmitter when the named event
    /// happens.
    fn on(&mut self, ev_name: &str, tx: Transmitter<Event>);

    /// Transmit an event message on the given transmitter when the named event
    /// happens on [`web_sys::Window`].
    fn window_on(&mut self, ev_name: &str, tx: Transmitter<Event>);

    /// Transmit an event message into the given transmitter when the named event
    /// happens on [`web_sys::Document`].
    fn document_on(&mut self, ev_name: &str, tx: Transmitter<Event>);
}

/// `PostBuildView`s can send their underlying browser DOM node as a message on
/// a Transmitter once they've been built.
///
/// `PostBuildView` allows you to construct component behaviors that operate on the constructed
/// node directly, while still keeping the definition in its place within your view
/// builder function. For example, you may want to use `input.focus()` within the
/// `update` function of your component. This method allows you to store the
/// input `HtmlInputElement` once it is built, allowing you to use it as you see fit
/// within your [`Component::update`][crate::component::Component::update] function.
pub trait PostBuildView {
    /// The type of the inner DOM node.
    type DomNode;

    /// After the view is built, transmit its underlying DomNode on the given
    /// transmitter.
    fn post_build(&mut self, tx: Transmitter<Self::DomNode>);
}

/// `ParentView`s can nest child views.
pub trait ParentView<T> {
    /// Add a child to this parent.
    fn with(&mut self, view_now: T);
}

/// Variants used to patch the children of a [`View`][crate::prelude::View].
#[derive(Clone, Debug)]
pub enum Patch<T> {
    /// Insert a child node at a certain index.
    /// Zero-indexed.
    Insert {
        /// The index to insert the child node.
        index: usize,
        /// The child node.
        value: T
    },
    /// Replace a child at a certain index.
    /// Zero-indexed.
    Replace {
        /// The index of the child to replace.
        index: usize,
        /// The replacement child.
        value: T
    },
    /// Remove the child at a certain index.
    /// Zero-indexed.
    Remove {
        /// The index of the child to remove.
        index: usize
    },
    /// Remove all child nodes.
    RemoveAll,
    /// Push a child node onto the front of the list of child nodes.
    PushFront {
        /// The child to push at the front.
        value: T
    },
    /// Push a child node onto the back of the list of child nodes.
    PushBack {
        /// The child to push on the back.
        value: T
    },
    /// Remove the first child node.
    PopFront,
    /// Remove the last child node.
    PopBack,
    /// Keep the first N nodes.
    Keep {
        /// The number/size of the first nodes to keep.
        first: usize,
    },
    /// Drop the first N nodes.
    Drop {
        /// the number/size of the first nodes to drop.
        first: usize,
    }
}

impl<T> Patch<T> {
    pub(crate) fn branch_map<F, X>(&self, f: F) -> Patch<X>
    where
        F: FnOnce(&T) -> X,
    {
        match self {
            Patch::Insert { index, value } => Patch::Insert {
                index: *index,
                value: f(value),
            },
            Patch::Replace { index, value } => Patch::Replace {
                index: *index,
                value: f(value),
            },
            Patch::Remove { index } => Patch::Remove { index: *index },
            Patch::RemoveAll => Patch::RemoveAll,
            Patch::PushFront { value } => Patch::PushFront { value: f(value) },
            Patch::PushBack { value } => Patch::PushBack { value: f(value) },
            Patch::PopFront => Patch::PopFront,
            Patch::PopBack => Patch::PopBack,
            Patch::Keep { first } => Patch::Keep{first: *first},
            Patch::Drop { first } => Patch::Drop{first: *first},
        }
    }
}

/// `PatchView`s' children can be manipulated using patch commands sent on a [`Receiver`].
pub trait PatchView<T> {
    /// Patch the view using the [`Patch`] messages sent on the given [`Receiver`].
    fn patch<S: Clone + Into<T> + 'static>(&mut self, rx: Receiver<Patch<S>>);
}