-
-
Notifications
You must be signed in to change notification settings - Fork 1.4k
/
main.rs
145 lines (131 loc) · 4.21 KB
/
main.rs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
use wasm_bindgen::JsCast;
use web_sys::{Element, ShadowRootInit, ShadowRootMode};
use yew::{create_portal, html, Children, Component, Context, Html, NodeRef, Properties};
#[derive(Properties, PartialEq)]
pub struct ShadowDOMProps {
#[prop_or_default]
pub children: Children,
}
pub struct ShadowDOMHost {
host_ref: NodeRef,
inner_host: Option<Element>,
}
impl Component for ShadowDOMHost {
type Message = ();
type Properties = ShadowDOMProps;
fn create(_: &Context<Self>) -> Self {
Self {
host_ref: NodeRef::default(),
inner_host: None,
}
}
fn rendered(&mut self, ctx: &Context<Self>, first_render: bool) {
if first_render {
let shadow_root = self
.host_ref
.get()
.expect("rendered host")
.unchecked_into::<Element>()
.attach_shadow(&ShadowRootInit::new(ShadowRootMode::Open))
.expect("installing shadow root succeeds");
let inner_host = gloo_utils::document()
.create_element("div")
.expect("can create inner wrapper");
shadow_root
.append_child(&inner_host)
.expect("can attach inner host");
self.inner_host = Some(inner_host);
ctx.link().send_message(());
}
}
fn update(&mut self, _: &Context<Self>, _: Self::Message) -> bool {
true
}
fn view(&self, ctx: &Context<Self>) -> Html {
let contents = if let Some(ref inner_host) = self.inner_host {
create_portal(
html! {
{for ctx.props().children.iter()}
},
inner_host.clone(),
)
} else {
html! { <></> }
};
html! {
<div ref={self.host_ref.clone()}>
{contents}
</div>
}
}
}
pub struct App {
style_html: Html,
title_element: Element,
counter: u32,
}
pub enum AppMessage {
IncreaseCounter,
}
impl Component for App {
type Message = AppMessage;
type Properties = ();
fn create(_ctx: &Context<Self>) -> Self {
let document_head = gloo_utils::document()
.head()
.expect("head element to be present");
let title_element = document_head
.query_selector("title")
.expect("to find a title element")
.expect("to find a title element");
title_element.set_text_content(None); // Clear the title element
let style_html = create_portal(
html! {
<style>{"p { color: red; }"}</style>
},
document_head.into(),
);
Self {
style_html,
title_element,
counter: 0,
}
}
fn update(&mut self, _ctx: &Context<Self>, msg: Self::Message) -> bool {
match msg {
AppMessage::IncreaseCounter => self.counter += 1,
}
true
}
fn view(&self, ctx: &Context<Self>) -> Html {
let onclick = ctx.link().callback(|_| AppMessage::IncreaseCounter);
let title = create_portal(
html! {
if self.counter > 0 {
{format!("Clicked {} times", self.counter)}
} else {
{"Yew • Portals"}
}
},
self.title_element.clone(),
);
html! {
<>
{self.style_html.clone()}
{title}
<p>{"This paragraph is colored red, and its style is mounted into "}<pre>{"document.head"}</pre>{" with a portal"}</p>
<div>
<ShadowDOMHost>
<p>{"This paragraph is rendered in a shadow dom and thus not affected by the surrounding styling context"}</p>
<span>{"Buttons clicked inside the shadow dom work fine."}</span>
<button {onclick}>{"Click me!"}</button>
</ShadowDOMHost>
<p>{format!("The button has been clicked {} times. This is also reflected in the title of the tab!", self.counter)}</p>
</div>
</>
}
}
}
fn main() {
yew::Renderer::<App>::new().render();
}