perspective_viewer/components/
modal.rs

1// ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
2// ┃ ██████ ██████ ██████       █      █      █      █      █ █▄  ▀███ █       ┃
3// ┃ ▄▄▄▄▄█ █▄▄▄▄▄ ▄▄▄▄▄█  ▀▀▀▀▀█▀▀▀▀▀ █ ▀▀▀▀▀█ ████████▌▐███ ███▄  ▀█ █ ▀▀▀▀▀ ┃
4// ┃ █▀▀▀▀▀ █▀▀▀▀▀ █▀██▀▀ ▄▄▄▄▄ █ ▄▄▄▄▄█ ▄▄▄▄▄█ ████████▌▐███ █████▄   █ ▄▄▄▄▄ ┃
5// ┃ █      ██████ █  ▀█▄       █ ██████      █      ███▌▐███ ███████▄ █       ┃
6// ┣━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┫
7// ┃ Copyright (c) 2017, the Perspective Authors.                              ┃
8// ┃ ╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌ ┃
9// ┃ This file is part of the Perspective library, distributed under the terms ┃
10// ┃ of the [Apache License 2.0](https://www.apache.org/licenses/LICENSE-2.0). ┃
11// ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
12
13use std::cell::Cell;
14use std::marker::PhantomData;
15use std::rc::Rc;
16
17use derivative::Derivative;
18use yew::html::*;
19use yew::prelude::*;
20use yew::virtual_dom::VChild;
21
22use crate::utils::WeakScope;
23
24#[derive(Clone, Default, Eq, PartialEq)]
25pub struct ModalOrientation(Rc<Cell<bool>>);
26
27impl ImplicitClone for ModalOrientation {}
28
29impl From<ModalOrientation> for bool {
30    fn from(x: ModalOrientation) -> Self {
31        x.0.get()
32    }
33}
34
35pub trait ModalLink<T>
36where
37    T: Component,
38{
39    fn weak_link(&self) -> &'_ WeakScope<T>;
40}
41
42pub trait SetModalLink {
43    fn set_modal_link(&self);
44}
45
46impl<T> SetModalLink for &Context<T>
47where
48    T: Component,
49    T::Properties: ModalLink<T>,
50{
51    fn set_modal_link(&self) {
52        *self.props().weak_link().borrow_mut() = Some(self.link().clone());
53    }
54}
55
56#[derive(Debug)]
57pub enum ModalMsg<T>
58where
59    T: Component,
60{
61    SetPos {
62        top: f64,
63        left: f64,
64        visible: bool,
65        rev_vert: bool,
66    },
67    SubMsg(T::Message),
68}
69
70#[derive(Properties, Derivative)]
71#[derivative(PartialEq(bound = ""))]
72pub struct ModalProps<T>
73where
74    T: Component,
75    T::Properties: ModalLink<T>,
76{
77    pub child: Option<VChild<T>>,
78}
79
80#[derive(Default)]
81pub struct Modal<T> {
82    css: String,
83    rev_vert: ModalOrientation,
84    _comp: PhantomData<T>,
85}
86
87impl<T> Component for Modal<T>
88where
89    T: Component,
90    T::Properties: ModalLink<T>,
91{
92    type Message = ModalMsg<T>;
93    type Properties = ModalProps<T>;
94
95    fn create(_ctx: &Context<Self>) -> Self {
96        Self {
97            css: ":host{{top:0px;left:0px;opacity:0}}".to_owned(),
98            rev_vert: Default::default(),
99            _comp: PhantomData,
100        }
101    }
102
103    fn update(&mut self, ctx: &Context<Self>, msg: ModalMsg<T>) -> bool {
104        match msg {
105            ModalMsg::SetPos {
106                top,
107                left,
108                visible,
109                rev_vert,
110            } => {
111                let opacity = if visible { "" } else { ";opacity:0" };
112                self.css = format!(":host{{top:{top}px;left:{left}px{opacity}}}");
113                self.rev_vert.0.set(rev_vert);
114                true
115            },
116            ModalMsg::SubMsg(msg) => {
117                if let Some(child) = &ctx.props().child
118                    && let Some(link) = child.props.weak_link().borrow().as_ref()
119                {
120                    link.send_message(msg);
121                }
122
123                false
124            },
125        }
126    }
127
128    fn view(&self, ctx: &Context<Self>) -> Html {
129        let child = ctx
130            .props()
131            .child
132            .clone()
133            .map(Html::from)
134            .unwrap_or_default();
135
136        html! {
137            <>
138                <style>{ self.css.to_owned() }</style>
139                <ContextProvider<ModalOrientation> context={&self.rev_vert}>
140                    <NoRender>{ child }</NoRender>
141                </ContextProvider<ModalOrientation>>
142            </>
143        }
144    }
145}
146
147#[derive(Properties, Clone)]
148pub struct NoRenderProps {
149    pub children: Children,
150}
151
152impl PartialEq for NoRenderProps {
153    fn eq(&self, _other: &Self) -> bool {
154        true
155    }
156}
157
158pub struct NoRender {}
159
160impl Component for NoRender {
161    type Message = ();
162    type Properties = NoRenderProps;
163
164    fn create(_ctx: &Context<Self>) -> Self {
165        Self {}
166    }
167
168    fn view(&self, ctx: &Context<Self>) -> Html {
169        html! { { ctx.props().children.iter().collect::<Html>() } }
170    }
171}