yew_styles/components/carousel/
carousel_controls.rs

1use crate::styles::{get_palette, get_size, get_style, Palette, Size, Style};
2use wasm_bindgen_test::*;
3use yew::prelude::*;
4use yew::{utils, App};
5use yew_assets::controller_assets::{ControllerAssets, ControllerIcon};
6
7/// # Carousel Controls
8///
9/// ## Features required
10///
11/// carousel
12///
13/// ## Example
14///
15/// ```rust
16/// use super::highlighters::get_carousel;
17/// use yew::prelude::*;
18/// use yew::services::ConsoleService;
19/// use yew::utils::document;
20/// use yew_prism::Prism;
21/// use yew_styles::carousel::{Carousel, CarouselControls, CarouselDot, CarouselImage};
22/// use yew_styles::styles::Size;
23///
24/// pub struct CarouselPage {
25///     link: ComponentLink<Self>,
26///     images: Vec<&'static str>,
27///     active_image: Vec<bool>,
28/// }
29///
30/// pub enum Msg {
31///     ChangeImage(usize),
32///     Scroll(WheelEvent),
33///     ShowScroll,
34///     HideScroll,
35///     Prev,
36///     Next,
37/// }
38///
39/// impl Component for CarouselPage {
40///     type Message = Msg;
41///     type Properties = ();
42///
43///     fn create(_props: Self::Properties, link: ComponentLink<Self>) -> Self {
44///         Self {
45///             link,
46///             images: vec!["/slide_1.jpg", "/slide_2.jpg", "/slide_3.jpg"],
47///             active_image: vec![true, false, false],
48///         }
49///     }
50///
51///     fn update(&mut self, msg: Self::Message) -> ShouldRender {
52///         match msg {
53///             Msg::ChangeImage(image_index) => {
54///                 for (i, _) in self.active_image.clone().into_iter().enumerate() {
55///                     self.active_image[i] = false;
56///                 }
57///
58///                 self.active_image[image_index] = true;
59///             }
60///             Msg::Prev => {
61///                 let len = self.active_image.len();
62///                 let index_opt = self.active_image.to_vec().into_iter().position(|ai| ai);
63///                 for (i, _) in self.active_image.clone().into_iter().enumerate() {
64///                     self.active_image[i] = false;
65///                 }
66///
67///                 if let Some(index) = index_opt {
68///                     if index == 0 {
69///                         self.active_image[len - 1] = true
70///                     } else {
71///                         self.active_image[index - 1] = true
72///                     }
73///                 } else {
74///                     ConsoleService::error("no image active")
75///                 }
76///             }
77///
78///             Msg::Next => {
79///                 let len = self.active_image.len();
80///                 let index_opt = self.active_image.to_vec().into_iter().position(|ai| ai);
81///
82///                 for (i, _) in self.active_image.clone().into_iter().enumerate() {
83///                     self.active_image[i] = false;
84///                 }
85///
86///                 if let Some(index) = index_opt {
87///                     if index == len - 1 {
88///                         self.active_image[0] = true
89///                     } else {
90///                         self.active_image[index + 1] = true
91///                     }
92///                 } else {
93///                     ConsoleService::error("no image active")
94///                 }
95///             }
96///             Msg::Scroll(wheel_event) => {
97///                 let len = self.active_image.len();
98///                 let index_opt = self.active_image.to_vec().into_iter().position(|ai| ai);
99///                 for (i, _) in self.active_image.clone().into_iter().enumerate() {
100///                     self.active_image[i] = false;
101///                 }
102///
103///                 if wheel_event.delta_y() > 0.00 {
104///                     if let Some(index) = index_opt {
105///                         if index == 0 {
106///                             self.active_image[len - 1] = true
107///                         } else {
108///                             self.active_image[index - 1] = true
109///                         }
110///                     } else {
111///                         ConsoleService::error("no image active")
112///                     }
113///                 } else if let Some(index) = index_opt {
114///                     if index == len - 1 {
115///                         self.active_image[0] = true
116///                     } else {
117///                         self.active_image[index + 1] = true
118///                     }
119///                 } else {
120///                     ConsoleService::error("no image active")
121///                 }
122///             }
123///             Msg::ShowScroll => {
124///                 let body_style = document().body().unwrap().style();
125///                 body_style.set_property("overflow", "hidden").unwrap();
126///             }
127///             Msg::HideScroll => {
128///                 let body_style = document().body().unwrap().style();
129///                 body_style.set_property("overflow", "scroll").unwrap();
130///             }
131///         }
132///
133///         true
134///     }
135///
136///     fn change(&mut self, _props: Self::Properties) -> ShouldRender {
137///         false
138///     }
139///
140///     fn view(&self) -> Html {
141///         html! {
142///             <div>
143///                 <Carousel
144///                     class_name="fill-background"
145///                     onwheel_signal= self.link.callback(Msg::Scroll)
146///                     onmouseover_signal= self.link.callback(|_| Msg::ShowScroll)
147///                     onmouseleave_signal= self.link.callback(|_| Msg::HideScroll)>
148///                     {get_images(self.images.to_vec(), self.active_image.to_vec())}
149///                     {get_dots(self.active_image.to_vec(), self.link.clone())}
150///                     {get_controls(self.link.clone())}
151///                 </Carousel>
152///             </div>
153///         }
154///     }
155/// }
156///
157/// fn get_images(images: Vec<&str>, active_image: Vec<bool>) -> Html {
158///     images
159///         .into_iter()
160///         .enumerate()
161///         .map(|(index, image)| {
162///             html! {
163///                 <CarouselImage active=active_image[index] img_src=image/>
164///             }
165///         })
166///         .collect::<Html>()
167/// }
168///
169/// fn get_dots(active_image: Vec<bool>, link: ComponentLink<CarouselPage>) -> Html {
170///     let mut dot = vec![];
171///
172///     for (i, _) in active_image.clone().into_iter().enumerate() {
173///         dot.push(html! {
174///             <CarouselDot active=active_image[i] onclick_signal = link.callback(move |_| Msg::ChangeImage(i))>
175///                 <CommunicationAssets size=("12".to_string(), "12".to_string()) icon=CommunicationIcon::Smile/>
176///             </CarouselDot>
177///         })
178///     }
179///
180///     dot.into_iter().collect::<Html>()
181/// }
182///
183/// fn get_controls(link: ComponentLink<App>) -> Html {
184///     html! {
185///         <CarouselControls
186///             controls_size=Size::Small
187///             prev_signal=link.callback(|_| Msg::Prev)
188///             next_signal=link.callback(|_| Msg::Next)/>
189///     }
190/// }
191/// ```
192pub struct CarouselControls {
193    link: ComponentLink<Self>,
194    props: Props,
195}
196
197#[derive(Clone, Properties, PartialEq)]
198pub struct Props {
199    pub prev_signal: Callback<MouseEvent>,
200    pub next_signal: Callback<MouseEvent>,
201    /// controls styles. Default `Style::Regular`
202    #[prop_or(Style::Regular)]
203    pub controls_style: Style,
204    /// Type controls style. Default `Palette::Standard`
205    #[prop_or(Palette::Standard)]
206    pub controls_palette: Palette,
207    /// Three diffent button standard sizes. Default `Size::Medium`
208    #[prop_or(Size::Medium)]
209    pub controls_size: Size,
210    /// General property to get the ref of the component
211    #[prop_or_default]
212    pub code_ref: NodeRef,
213    /// General property to add keys
214    #[prop_or_default]
215    pub key: String,
216    /// General property to add custom class styles
217    #[prop_or_default]
218    pub class_name: String,
219    /// General property to add custom id
220    #[prop_or_default]
221    pub id: String,
222}
223
224pub enum Msg {
225    PrevClicked(MouseEvent),
226    NextClicked(MouseEvent),
227}
228
229impl Component for CarouselControls {
230    type Message = Msg;
231    type Properties = Props;
232
233    fn create(props: Self::Properties, link: ComponentLink<Self>) -> Self {
234        Self { link, props }
235    }
236
237    fn update(&mut self, msg: Self::Message) -> ShouldRender {
238        match msg {
239            Msg::PrevClicked(mouse_event) => {
240                self.props.prev_signal.emit(mouse_event);
241            }
242            Msg::NextClicked(mouse_event) => {
243                self.props.next_signal.emit(mouse_event);
244            }
245        }
246
247        true
248    }
249
250    fn change(&mut self, props: Self::Properties) -> ShouldRender {
251        if self.props != props {
252            self.props = props;
253            return true;
254        }
255
256        false
257    }
258
259    fn view(&self) -> Html {
260        html! {
261            <div class="carousel-control"
262                key=self.props.key.clone()
263                id=self.props.id.clone()
264                ref=self.props.code_ref.clone()
265            >
266                <div
267                    class="carousel-control-left-container"
268
269                    onclick=self.link.callback(Msg::PrevClicked)>
270                    <ControllerAssets
271                        size=("50".to_string(),"50".to_string())
272                        class_name=format!("carousel-control-left {} {} {} {}",
273                            get_size(self.props.controls_size.clone()),
274                            get_style(self.props.controls_style.clone()),
275                            get_palette(self.props.controls_palette.clone()),
276                            self.props.class_name.clone(),
277                        )
278                        icon=ControllerIcon::ChevronLeft
279                    />
280                </div>
281                <div
282                    class="carousel-control-right-container"
283                    onclick=self.link.callback(Msg::NextClicked)
284                >
285                    <ControllerAssets
286                        size=("50".to_string(),"50".to_string())
287                        class_name=format!("carousel-control-right {} {} {} {}",
288                        get_size(self.props.controls_size.clone()),
289                        get_style(self.props.controls_style.clone()),
290                        get_palette(self.props.controls_palette.clone()),
291                        self.props.class_name.clone(),
292                    )
293                        icon=ControllerIcon::ChevronRight/>
294                </div>
295            </div>
296        }
297    }
298}
299
300#[wasm_bindgen_test]
301fn should_create_carousel_controls_component() {
302    let props = Props {
303        code_ref: NodeRef::default(),
304        class_name: String::from("test-carousel"),
305        id: String::from("carousel-id-test"),
306        controls_palette: Palette::Standard,
307        controls_style: Style::Regular,
308        controls_size: Size::Medium,
309        next_signal: Callback::noop(),
310        prev_signal: Callback::noop(),
311        key: "".to_string(),
312    };
313
314    let carousel: App<CarouselControls> = App::new();
315    carousel.mount_with_props(
316        utils::document().get_element_by_id("output").unwrap(),
317        props,
318    );
319
320    let carousel_element = utils::document()
321        .get_element_by_id("carousel-id-test")
322        .unwrap();
323
324    assert_eq!(carousel_element.id(), "carousel-id-test");
325}