diff options
Diffstat (limited to 'web')
| -rw-r--r-- | web/src/css.rs | 44 | ||||
| -rw-r--r-- | web/src/widget.rs | 3 | ||||
| -rw-r--r-- | web/src/widget/toggler.rs | 168 | 
3 files changed, 215 insertions, 0 deletions
diff --git a/web/src/css.rs b/web/src/css.rs index 66c363f2..21f51f85 100644 --- a/web/src/css.rs +++ b/web/src/css.rs @@ -14,6 +14,9 @@ pub enum Rule {      /// Spacing between elements      Spacing(u16), + +    /// Toggler input for a specific size +    Toggler(u16),  }  impl Rule { @@ -23,6 +26,7 @@ impl Rule {              Rule::Column => String::from("c"),              Rule::Row => String::from("r"),              Rule::Spacing(spacing) => format!("s-{}", spacing), +            Rule::Toggler(size) => format!("toggler-{}", size),          }      } @@ -55,6 +59,46 @@ impl Rule {                  class              )              .into_bump_str(), +            Rule::Toggler(size) => bumpalo::format!( +                in bump, +                ".toggler-{} {{ display: flex; cursor: pointer; justify-content: space-between; }} \ +                 .toggler-{} input {{ display:none; }} \ +                 .toggler-{} span {{ background-color: #b1b1b1; position: relative; display: inline-flex; width:{}px; height: {}px; border-radius: {}px;}} \ +                 .toggler-{} span > span {{ background-color: #FFFFFF; width: {}px; height: {}px; border-radius: 50%; top: 1px; left: 1px;}} \ +                 .toggler-{}:hover span > span {{ background-color: #f1f1f1 !important; }} \ +                 .toggler-{} input:checked + span {{ background-color: #00FF00; }} \ +                 .toggler-{} input:checked + span > span {{ -webkit-transform: translateX({}px); -ms-transform:translateX({}px); transform: translateX({}px); }} +                ", +                // toggler +                size, + +                // toggler input +                size, + +                // toggler span +                size, +                size*2, +                size, +                size, + +                // toggler span > span +                size, +                size-2, +                size-2, + +                // toggler: hover + span > span +                size, + +                // toggler input:checked + span +                size, + +                // toggler input:checked + span > span +                size, +                size, +                size, +                size +            ) +            .into_bump_str(),          }      }  } diff --git a/web/src/widget.rs b/web/src/widget.rs index 023f5f13..4cb0a9cc 100644 --- a/web/src/widget.rs +++ b/web/src/widget.rs @@ -24,6 +24,7 @@ pub mod radio;  pub mod scrollable;  pub mod slider;  pub mod text_input; +pub mod toggler;  mod column;  mod row; @@ -40,6 +41,8 @@ pub use slider::Slider;  pub use text::Text;  #[doc(no_inline)]  pub use text_input::TextInput; +#[doc(no_inline)] +pub use toggler::Toggler;  pub use checkbox::Checkbox;  pub use column::Column; diff --git a/web/src/widget/toggler.rs b/web/src/widget/toggler.rs new file mode 100644 index 00000000..5bcc1fc7 --- /dev/null +++ b/web/src/widget/toggler.rs @@ -0,0 +1,168 @@ +//! Show toggle controls using togglers. +use crate::{css, Bus, Css, Element, Length, Widget}; + +pub use iced_style::toggler::{Style, StyleSheet}; + +use dodrio::bumpalo; +use std::rc::Rc; + +/// A toggler that can be toggled. +///  +/// # Example +///  +/// ``` +/// # use iced_web::Toggler; +///  +/// pub enum Message { +///     TogglerToggled(bool), +/// } +///  +/// let is_active = true; +///  +/// Toggler::new(is_active, String::from("Toggle me!"), Message::TogglerToggled); +/// ``` +///  +#[allow(missing_debug_implementations)] +pub struct Toggler<Message> { +    is_active: bool, +    on_toggle: Rc<dyn Fn(bool) -> Message>, +    label: Option<String>, +    id: Option<String>, +    width: Length, +    style: Box<dyn StyleSheet>, +} + +impl<Message> Toggler<Message> { +    /// Creates a new [`Toggler`]. +    ///  +    /// It expects: +    ///   * a boolean describing whether the [`Toggler`] is active or not +    ///   * An optional label for the [`Toggler`] +    ///   * a function that will be called when the [`Toggler`] is toggled. It +    ///     will receive the new state of the [`Toggler`] and must produce a +    ///     `Message`. +    ///  +    /// [`Toggler`]: struct.Toggler.html +    pub fn new<F>(is_active: bool, label: impl Into<Option<String>>, f: F) -> Self +    where +        F: 'static + Fn(bool) -> Message, +    { +        Toggler { +            is_active, +            on_toggle: Rc::new(f), +            label: label.into(), +            id: None, +            width: Length::Shrink, +            style: Default::default(), +        } +    } + +    /// Sets the width of the [`Toggler`]. +    ///  +    /// [`Toggler`]: struct.Toggler.html +    pub fn width(mut self, width: Length) -> Self { +        self.width = width; +        self +    } + +    /// Sets the style of the [`Toggler`]. +    ///  +    /// [`Toggler`]: struct.Toggler.html +    pub fn style(mut self, style: impl Into<Box<dyn StyleSheet>>) -> Self { +        self.style = style.into(); +        self +    } + +    /// Sets the id of the [`Toggler`]. +    ///  +    /// [`Toggler`]: struct.Toggler.html +    pub fn id(mut self, id: impl Into<String>) -> Self { +        self.id = Some(id.into()); +        self +    } +} + +impl<Message> Widget<Message> for Toggler<Message> +where +    Message: 'static, +{ +    fn node<'b>( +        &self, +        bump: &'b bumpalo::Bump, +        bus: &Bus<Message>, +        style_sheet: &mut Css<'b>, +    ) -> dodrio::Node<'b> { +        use dodrio::builder::*; +        use dodrio::bumpalo::collections::String; + +        let toggler_label = &self.label.as_ref().map(|label| { +            String::from_str_in(&label, bump).into_bump_str() +        }); + +        let event_bus = bus.clone(); +        let on_toggle = self.on_toggle.clone(); +        let is_active = self.is_active; + +        let row_class = style_sheet.insert(bump, css::Rule::Row); +        let toggler_class = style_sheet.insert(bump, css::Rule::Toggler(16)); + +        let (label, input) = if let Some(id) = &self.id { +            let id = String::from_str_in(id, bump).into_bump_str(); + +            (label(bump).attr("for", id), input(bump).attr("id", id)) +        } else { +            (label(bump), input(bump)) +        }; + +        let checkbox = input +            .attr("type", "checkbox") +            .bool_attr("checked", self.is_active) +            .on("click", move |_root, vdom, _event| { +                let msg = on_toggle(!is_active); +                event_bus.publish(msg); + +                vdom.schedule_render(); +            }) +            .finish(); + +        let toggler = span(bump) +            .children(vec![span(bump).finish()]) +            .finish(); + +        label +            .attr( +                "class", +                bumpalo::format!(in bump, "{} {}", row_class, toggler_class) +                    .into_bump_str(), +            ) +            .attr( +                "style", +                bumpalo::format!(in bump, "width: {}; align-items: center", css::length(self.width)) +                .into_bump_str() +            ) +            .children(                 +                if let Some(label) = toggler_label { +                    vec![ +                        text(label), +                        checkbox, +                        toggler, +                    ] +                } else { +                    vec![ +                        checkbox, +                        toggler, +                    ] +                } +            ) +            .finish() +    } +} + +impl<'a, Message> From<Toggler<Message>> for Element<'a, Message> +where +    Message: 'static, +{ +    fn from(toggler: Toggler<Message>) -> Element<'a, Message> { +        Element::new(toggler) +    } +}
\ No newline at end of file  | 
