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
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
#![allow(
    clippy::redundant_closure,
    clippy::needless_update,
    clippy::inconsistent_struct_constructor,
    clippy::type_complexity,
    clippy::derive_partial_eq_without_eq,
    clippy::uninlined_format_args,
    clippy::derivable_impls,
    clippy::enum_variant_names
)]

mod alert;
mod button_group;
mod buttons;
mod callout;
mod card;
mod checkbox;
mod collapse;
mod control_group;
mod dialog;
mod divider;
mod html_elements;
mod html_select;
mod icon;
mod input_group;
mod menu;
mod numeric_input;
mod overlay;
mod panel_stack;
mod portal;
mod progress_bar;
mod radio;
mod radio_group;
mod slider;
mod spinner;
mod switch;
mod tabs;
mod tag;
mod text;
mod text_area;
#[cfg(feature = "tree")]
mod tree;

pub use alert::*;
pub use button_group::*;
pub use buttons::*;
pub use callout::*;
pub use card::*;
pub use checkbox::*;
pub use collapse::*;
pub use control_group::*;
pub use dialog::*;
pub use divider::*;
pub use html_elements::*;
pub use html_select::*;
pub use icon::*;
#[cfg(feature = "tree")]
pub use id_tree;
pub use input_group::*;
pub use menu::*;
pub use numeric_input::*;
pub use overlay::*;
pub use panel_stack::*;
pub use portal::*;
pub use progress_bar::*;
pub use radio::*;
pub use radio_group::*;
pub use slider::*;
pub use spinner::*;
pub use switch::*;
pub use tabs::*;
pub use tag::*;
pub use text::*;
pub use text_area::*;
#[cfg(feature = "tree")]
pub use tree::*;

use implicit_clone::ImplicitClone;
use std::cell::Cell;
use yew::classes;
use yew::Classes;

// See https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/buttons
#[allow(dead_code)]
const MOUSE_EVENT_BUTTONS_NONE: u16 = 0;
#[allow(dead_code)]
const MOUSE_EVENT_BUTTONS_PRIMARY: u16 = 1;
#[allow(dead_code)]
const MOUSE_EVENT_BUTTONS_SECONDARY: u16 = 2;
#[allow(dead_code)]
const MOUSE_EVENT_BUTTONS_AUXILIARY: u16 = 4;
#[allow(dead_code)]
const MOUSE_EVENT_BUTTONS_FOURTH: u16 = 8;
#[allow(dead_code)]
const MOUSE_EVENT_BUTTONS_FIFTH: u16 = 16;

#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
pub enum Intent {
    Primary,
    Success,
    Warning,
    Danger,
}

impl ImplicitClone for Intent {}

impl From<Intent> for Classes {
    fn from(intent: Intent) -> Self {
        use Intent::*;
        Classes::from(match intent {
            Primary => "bp3-intent-primary",
            Success => "bp3-intent-success",
            Warning => "bp3-intent-warning",
            Danger => "bp3-intent-danger",
        })
    }
}

#[derive(Clone, Copy, PartialEq, Eq, Debug, Hash)]
pub enum Elevation {
    Level0,
    Level1,
    Level2,
    Level3,
    Level4,
}

impl Elevation {
    /// Return the next highest `Elevation`.
    /// ```
    /// # use yewprint::Elevation;
    /// assert_eq!(Elevation::Level1.above(), Elevation::Level2);
    /// assert_eq!(Elevation::Level4.above(), Elevation::Level4);
    /// ```
    pub fn above(&self) -> Self {
        use Elevation::*;
        match self {
            Level0 => Level1,
            Level1 => Level2,
            Level2 => Level3,
            Level3 => Level4,
            Level4 => Level4,
        }
    }

    /// Return the next lowest `Elevation`.
    /// ```
    /// # use yewprint::Elevation;
    /// assert_eq!(Elevation::Level3.below(), Elevation::Level2);
    /// assert_eq!(Elevation::Level0.below(), Elevation::Level0);
    /// ```
    pub fn below(&self) -> Self {
        use Elevation::*;
        match self {
            Level0 => Level0,
            Level1 => Level0,
            Level2 => Level1,
            Level3 => Level2,
            Level4 => Level3,
        }
    }
}

impl Default for Elevation {
    fn default() -> Self {
        Self::Level0
    }
}

impl ImplicitClone for Elevation {}

impl From<Elevation> for Classes {
    fn from(elevation: Elevation) -> Self {
        use Elevation::*;
        Self::from(match elevation {
            Level0 => "bp3-elevation-0",
            Level1 => "bp3-elevation-1",
            Level2 => "bp3-elevation-2",
            Level3 => "bp3-elevation-3",
            Level4 => "bp3-elevation-4",
        })
    }
}

impl From<&Elevation> for Classes {
    fn from(elevation: &Elevation) -> Self {
        Self::from(*elevation)
    }
}

pub struct Dark;

impl Dark {
    pub fn with<T>(&self, f: impl FnOnce(&Cell<bool>) -> T) -> T {
        thread_local! {
            static DARK: Cell<bool> = Cell::new(false);
        }
        DARK.with(f)
    }

    pub fn auto_detect(&self) {
        let prefers_dark = web_sys::window()
            .and_then(|x| x.match_media("(prefers-color-scheme: dark)").ok().flatten())
            .map(|x| x.matches())
            .unwrap_or(true);
        self.set(prefers_dark);
    }

    pub fn get(&self) -> bool {
        self.with(|x| x.get())
    }

    pub fn set(&self, value: bool) {
        self.with(|x| x.set(value))
    }

    pub fn replace(&self, value: bool) -> bool {
        self.with(|x| x.replace(value))
    }

    pub fn toggle(&self) -> bool {
        self.with(|x| {
            let value = x.get();
            x.set(!value);
            value
        })
    }

    pub fn classes(&self) -> Classes {
        self.classes_with_override(None)
    }

    pub fn classes_with_override(&self, force: impl Into<Option<bool>>) -> Classes {
        if force.into().unwrap_or(self.get()) {
            thread_local! {
                static CLASSES: Classes = classes!("bp3-dark");
            }
            CLASSES.with(|x| x.clone())
        } else {
            thread_local! {
                static CLASSES: Classes = classes!();
            }
            CLASSES.with(|x| x.clone())
        }
    }
}