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
use crate::{random_id, Icon};
use web_sys::HtmlInputElement;
use yew::prelude::*;

#[derive(Clone, PartialEq, Properties)]
pub struct Props {
    #[prop_or_default]
    pub id: Option<String>,

    #[prop_or_default]
    pub checked: bool,

    #[prop_or_default]
    pub label: Option<String>,
    #[prop_or_default]
    pub label_off: Option<String>,
    #[prop_or_default]
    pub disabled: bool,

    #[prop_or_default]
    pub on_change: Callback<bool>,

    #[prop_or_default]
    pub aria_label: String,
}

pub struct Switch {
    id: String,
    input_ref: NodeRef,
}

pub enum Msg {
    Changed,
}

impl Component for Switch {
    type Message = Msg;
    type Properties = Props;

    fn create(ctx: &Context<Self>) -> Self {
        let id = ctx
            .props()
            .id
            .as_ref()
            .cloned()
            .unwrap_or_else(|| random_id());
        Self {
            id,
            input_ref: Default::default(),
        }
    }

    fn update(&mut self, ctx: &Context<Self>, msg: Self::Message) -> bool {
        match msg {
            Msg::Changed => {
                ctx.props().on_change.emit(self.current_state());
                return false;
            }
        }
    }

    fn changed(&mut self, ctx: &Context<Self>) -> bool {
        self.id = ctx
            .props()
            .id
            .as_ref()
            .cloned()
            .unwrap_or_else(|| self.id.clone());
        true
    }

    fn view(&self, ctx: &Context<Self>) -> Html {
        html! {
            <label class="pf-c-switch" for={self.id.clone()}>
                <input
                    ref={self.input_ref.clone()}
                    class="pf-c-switch__input"
                    type="checkbox"
                    id={self.id.clone()}
                    aria-label={ctx.props().aria_label.clone()}
                    checked={ctx.props().checked}
                    disabled={ctx.props().disabled}
                    onchange={ctx.link().callback(|_|Msg::Changed)}
                    />
                <span class="pf-c-switch__toggle">
                    if ctx.props().label.is_none() {
                        <span class="pf-c-switch__toggle-icon">
                            { Icon::Check }
                        </span>
                    }
                </span>
                if let Some(ref label) = ctx.props().label {
                    <>
                    <span class="pf-c-switch__label pf-m-on">{ label }</span>
                    <span class="pf-c-switch__label pf-m-off">{ ctx.props().label_off.as_ref().unwrap_or_else(||label) }</span>
                    </>
                }
            </label>
        }
    }
}

impl Switch {
    fn current_state(&self) -> bool {
        self.input_ref
            .cast::<HtmlInputElement>()
            .map(|input| input.checked())
            .unwrap_or_default()
    }
}