pub mod component;
use crate::{console, prelude::*, sys};
use popper_rs_sys::ModifierArguments;
use std::cell::RefCell;
use std::rc::Rc;
use wasm_bindgen::JsValue;
use wasm_bindgen::prelude::*;
use yew::prelude::*;
#[derive(Clone)]
pub struct UsePopper {
pub instance: UsePopperInstance,
pub state: UseStateHandle<State>,
}
#[derive(Clone)]
pub struct UsePopperInstance(pub Rc<RefCell<Option<Instance>>>);
impl PartialEq for UsePopperInstance {
fn eq(&self, other: &Self) -> bool {
Rc::ptr_eq(&self.0, &other.0)
}
}
impl UsePopperInstance {
pub fn force_update(&self) {
if let Some(instance) = &*self.0.borrow() {
instance.force_update();
}
}
pub async fn update(&self) {
let instance = (*self.0.borrow()).clone();
if let Some(instance) = instance {
instance.update().await;
}
}
}
#[hook]
pub fn use_popper(
reference: NodeRef,
popper: NodeRef,
options: Rc<Options>,
) -> Result<UsePopper, JsValue> {
let state = use_state_eq(State::default);
let onstatechange = use_memo((), |()| {
let state = state.clone();
Closure::wrap(Box::new(move |args: ModifierArguments| {
let s = args.instance().state();
console::debug!("updateState:", &s);
state.set(s.into());
}) as Box<dyn Fn(ModifierArguments)>)
});
let options: Rc<Result<JsValue, JsValue>> = {
use_memo(
(options, ModifierFn(onstatechange.clone())),
|(opts, onstatechange)| {
let mut opts: Options = (**opts).clone();
opts.modifiers.push(Modifier::Custom {
name: "applyStyles".into(),
phase: None,
enabled: Some(false),
r#fn: None,
});
opts.modifiers.push(Modifier::Custom {
name: "updateState".into(),
phase: Some("write".into()),
enabled: Some(true),
r#fn: Some(onstatechange.clone()),
});
opts.try_into()
},
)
};
let options = match options.as_ref() {
Ok(options) => options.clone(),
Err(err) => return Err(err.clone()),
};
let instance = use_mut_ref(|| Option::<Instance>::None);
{
let instance = instance.clone();
use_effect_with(
(reference, popper, options),
move |(reference, popper, options)| {
let reference = reference.get();
let popper = popper.get();
let result = if let (Some(reference), Some(popper)) = (reference, popper) {
console::debug!("Creating popper instance");
let instance = sys::create_popper(&reference, &popper, options);
instance.force_update();
Some(instance)
} else {
None
};
*instance.borrow_mut() = result.clone();
move || {
if let Some(result) = result {
console::debug!("Destroying popper instance");
result.destroy();
}
}
},
);
}
Ok(UsePopper {
instance: UsePopperInstance(instance),
state,
})
}