perspective_viewer/components/
modal.rs1use 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}