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}