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}