intuitive/components/experimental_components/modal/
hook.rs

1use std::sync::Arc;
2
3use parking_lot::Mutex;
4
5use crate::{components::Any as AnyComponent, state::State};
6
7static FUNCS: Mutex<Option<Funcs>> = Mutex::new(None);
8
9/// A structure returned by [`use_modal`] that controls the hiding/showing of a modal.
10///
11/// [`use_modal`]: fn.use_modal.html
12#[derive(Clone)]
13pub struct Funcs {
14  modal: State<Option<AnyComponent>>,
15
16  show: Arc<dyn Fn(AnyComponent) + Send + Sync>,
17  hide: Arc<dyn Fn() + Send + Sync>,
18}
19
20impl Funcs {
21  pub(crate) fn new(modal: State<Option<AnyComponent>>) -> Self {
22    let show_modal = modal.clone();
23    let hide_modal = modal.clone();
24
25    Self {
26      modal,
27      show: Arc::new(move |component| show_modal.set(Some(component))),
28      hide: Arc::new(move || hide_modal.set(None)),
29    }
30  }
31
32  /// Return whether if the modal is shown.
33  pub fn is_shown(&self) -> bool {
34    self.modal.inspect(Option::is_some)
35  }
36
37  /// Set `component` to be shown by the modal.
38  pub fn show(&self, component: AnyComponent) {
39    (self.show)(component);
40  }
41
42  /// Hide the showing modal, if any.
43  pub fn hide(&self) {
44    (self.hide)();
45  }
46}
47
48/// A hook that can control the hiding/showing of a modal.
49///
50/// Like [`use_state`], calls to `use_modal` may only be within a call to
51/// [`Component::render`]. Unlike [`use_state`], calls to `use_modal` may only be within
52/// a component that is a child component of some [`Modal`]. The [`Funcs`] returned by
53/// `use_modal` will then refer to the nearest ancestor [`Modal`]. For example, if we
54/// have the following layout:
55/// ```rust
56/// # use intuitive::{render, component, components::{Empty, experimental::modal::{use_modal, Modal}}};
57/// #
58/// #[component(MyComponent)]
59/// fn render() {
60///   let modal = use_modal();
61///
62///   render! {
63///     Empty()
64///   }
65/// }
66///
67/// #[component(Root)]
68/// fn render() {
69///   render! {
70///     Modal() {     // modal 1
71///       Modal() {   // modal 2
72///         Modal() { // modal 3
73///           MyComponent()
74///         }
75///       }
76///     }
77///   }
78/// }
79/// ```
80/// and `use_modal` is called within `MyComponent`, then it will return a [`Funcs`] struct
81/// that acts on `modal 3`. The other two ancestor modals are inaccessible.
82///
83/// [`Component::render`]: trait.Component.html#tymethod.render
84/// [`Modal`]: struct.Modal.html
85/// [`Funcs`]: struct.Funcs.html
86/// [`use_state`]: ../../state/fn.use_state.html
87pub fn use_modal() -> Funcs {
88  FUNCS
89    .lock()
90    .clone()
91    .expect("use modal called outside of a Modal or outside of render")
92}
93
94pub fn set_modal_funcs(funcs: Funcs) {
95  *FUNCS.lock() = Some(funcs);
96}