popper_rs/modifier/
mod.rs

1//! Modifiers
2
3mod offset;
4mod prevent_overflow;
5mod same_width;
6
7pub use offset::*;
8pub use prevent_overflow::*;
9pub use same_width::*;
10
11use crate::sys::ModifierArguments;
12use gloo_utils::format::JsValueSerdeExt;
13use serde_json::json;
14use std::borrow::Cow;
15use std::rc::Rc;
16use wasm_bindgen::closure::Closure;
17use wasm_bindgen::JsValue;
18
19/// Definition of a modifier function.
20///
21/// A modifier function will be called from popper.js and thus needs to be valid for the lifetime
22/// of the popper instance. Dropping the closure will invalidate the function and it will no longer
23/// be executed. This means that you need to keep a reference to the function for as long as it
24/// should be in used.
25#[derive(Clone, Debug)]
26pub struct ModifierFn(#[allow(clippy::type_complexity)] pub Rc<Closure<dyn Fn(ModifierArguments)>>);
27
28impl PartialEq for ModifierFn {
29    fn eq(&self, other: &Self) -> bool {
30        Rc::ptr_eq(&self.0, &other.0)
31    }
32}
33
34/// Definition of a modifier effect function.
35///
36/// A modifier function will be called from popper.js and thus needs to be valid for the lifetime
37/// of the popper instance. Dropping the closure will invalidate the function and it will no longer
38/// be executed. This means that you need to keep a reference to the function for as long as it
39/// should be in used.
40#[derive(Clone, Debug)]
41pub struct EffectFn(#[allow(clippy::type_complexity)] pub Rc<Closure<dyn Fn(ModifierArguments)>>);
42
43impl PartialEq for EffectFn {
44    fn eq(&self, other: &Self) -> bool {
45        Rc::ptr_eq(&self.0, &other.0)
46    }
47}
48
49/// Standard modifiers
50#[derive(Clone, Debug, PartialEq)]
51pub enum Modifier {
52    Offset(Offset),
53    PreventOverflow(PreventOverflow),
54    SameWidth(SameWidth),
55    Custom {
56        name: Cow<'static, str>,
57        phase: Option<Cow<'static, str>>,
58        enabled: Option<bool>,
59        r#fn: Option<ModifierFn>,
60    },
61}
62
63impl Modifier {
64    pub fn to_value(&self) -> Result<JsValue, JsValue> {
65        match self {
66            Self::Custom {
67                name,
68                phase,
69                enabled,
70                r#fn,
71            } => {
72                let m1 = js_sys::Object::new();
73                js_sys::Reflect::set(&m1, &"name".into(), &JsValue::from_str(name))?;
74                if let Some(phase) = phase {
75                    js_sys::Reflect::set(&m1, &"phase".into(), &JsValue::from_str(phase))?;
76                }
77                if let Some(enabled) = enabled {
78                    js_sys::Reflect::set(&m1, &"enabled".into(), &JsValue::from_bool(*enabled))?;
79                }
80                if let Some(r#fn) = r#fn {
81                    js_sys::Reflect::set(&m1, &"fn".into(), (*r#fn.0).as_ref())?;
82                }
83
84                Ok(m1.into())
85            }
86            Self::Offset(options) => JsValue::from_serde(&json!({
87                "name": "offset",
88                "options": options.to_json(),
89            }))
90            .map_err(|err| JsValue::from_str(&err.to_string())),
91            Self::PreventOverflow(options) => JsValue::from_serde(&json!({
92                "name": "preventOverflow",
93                "options": options.to_json(),
94            }))
95            .map_err(|err| JsValue::from_str(&err.to_string())),
96
97            Self::SameWidth(options) => Ok(options.to_js_value()),
98        }
99    }
100}