use std::cmp::max;
use std::rc::Rc;
use yew::prelude::*;
use super::{use_event, use_mut_latest};
#[derive(PartialEq, Eq, Clone, Debug)]
pub enum UseSwipeDirection {
Up,
Right,
Down,
Left,
None,
}
#[derive(Default)]
pub struct UseSwipeOptions {
pub threshold: Option<u32>,
pub onswipestart: Option<Box<dyn FnMut(TouchEvent)>>,
pub onswipe: Option<Box<dyn FnMut(TouchEvent)>>,
pub onswipeend: Option<Box<dyn FnMut(TouchEvent, UseSwipeDirection)>>,
}
pub struct UseSwipeHandle {
pub swiping: UseStateHandle<bool>,
pub direction: UseStateHandle<UseSwipeDirection>,
pub coords_start: UseStateHandle<(i32, i32)>,
pub coords_end: UseStateHandle<(i32, i32)>,
pub length_x: UseStateHandle<i32>,
pub length_y: UseStateHandle<i32>,
}
impl Clone for UseSwipeHandle {
fn clone(&self) -> Self {
Self {
swiping: self.swiping.clone(),
direction: self.direction.clone(),
coords_start: self.coords_start.clone(),
coords_end: self.coords_end.clone(),
length_x: self.length_x.clone(),
length_y: self.length_y.clone(),
}
}
}
#[hook]
pub fn use_swipe(node: NodeRef) -> UseSwipeHandle {
use_swipe_with_options(node, UseSwipeOptions::default())
}
#[hook]
pub fn use_swipe_with_window() -> UseSwipeHandle {
use_swipe_with_options(NodeRef::default(), UseSwipeOptions::default())
}
#[hook]
pub fn use_swipe_with_options(node: NodeRef, options: UseSwipeOptions) -> UseSwipeHandle {
let swiping = use_state_eq(|| false);
let direction = use_state_eq(|| UseSwipeDirection::None);
let coords_start = use_state(|| (0, 0));
let coords_end = use_state(|| (0, 0));
let length_x = use_state(|| 0);
let length_y = use_state(|| 0);
let threshold = options.threshold.unwrap_or(50);
let onswipestart = use_mut_latest(options.onswipestart);
let onswipe = use_mut_latest(options.onswipe);
let onswipeend = use_mut_latest(options.onswipeend);
let diff_x = {
let coords_start = coords_start.clone();
let coords_end = coords_end.clone();
Rc::new(move || (coords_start.0 - coords_end.0) as i32)
};
let diff_y = {
let coords_start = coords_start.clone();
let coords_end = coords_end.clone();
Rc::new(move || (coords_start.1 - coords_end.1) as i32)
};
let ontouchend = {
let swiping = swiping.clone();
let direction = direction.clone();
Rc::new(move |e: TouchEvent| {
if *swiping {
let onswipeend = onswipeend.current();
let onswipeend = &mut *onswipeend.borrow_mut();
if let Some(onswipeend) = onswipeend {
onswipeend(e, (*direction).clone());
}
}
swiping.set(false);
direction.set(UseSwipeDirection::None);
})
};
let threshold_exceeded = {
let diff_x = diff_x.clone();
let diff_y = diff_y.clone();
Rc::new(move || max(diff_x().abs(), diff_y().abs()) >= (threshold as i32))
};
{
let node = node.clone();
let coords_start = coords_start.clone();
let coords_end = coords_end.clone();
use_event(node, "touchstart", move |e: TouchEvent| {
let x = e.touches().get(0).map_or(0, |t| t.client_x());
let y = e.touches().get(0).map_or(0, |t| t.client_y());
coords_start.set((x, y));
coords_end.set((x, y));
let onswipestart = onswipestart.current();
let onswipestart = &mut *onswipestart.borrow_mut();
if let Some(onswipestart) = onswipestart {
onswipestart(e);
}
});
}
{
let node = node.clone();
let coords_end = coords_end.clone();
let swiping = swiping.clone();
let length_x = length_x.clone();
let length_y = length_y.clone();
let direction = direction.clone();
use_event(node, "touchmove", move |e: TouchEvent| {
let x = e.touches().get(0).map_or(0, |t| t.client_x());
let y = e.touches().get(0).map_or(0, |t| t.client_y());
coords_end.set((x, y));
length_x.set(diff_x());
length_y.set(diff_y());
if !*swiping && threshold_exceeded() {
swiping.set(true);
}
if !threshold_exceeded() {
direction.set(UseSwipeDirection::None);
} else if diff_x().abs() > diff_y().abs() {
if diff_x() > 0 {
direction.set(UseSwipeDirection::Left);
} else {
direction.set(UseSwipeDirection::Right);
}
} else if diff_y() > 0 {
direction.set(UseSwipeDirection::Up);
} else {
direction.set(UseSwipeDirection::Down);
}
if *swiping {
let onswipe = onswipe.current();
let onswipe = &mut *onswipe.borrow_mut();
if let Some(onswipe) = onswipe {
onswipe(e);
}
}
});
}
{
let node = node.clone();
let ontouchend = ontouchend.clone();
use_event(node, "touchend", move |e: TouchEvent| {
ontouchend(e);
});
}
{
let ontouchend = ontouchend.clone();
use_event(node, "touchcancel", move |e: TouchEvent| {
ontouchend(e);
});
}
UseSwipeHandle {
swiping,
direction,
coords_start,
coords_end,
length_x,
length_y,
}
}