Skip to content

Commit 962188f

Browse files
committed
wip: keyed-elements working
1 parent 42f943a commit 962188f

File tree

36 files changed

+1362
-2030
lines changed

36 files changed

+1362
-2030
lines changed

Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

examples/keyed-elements/Cargo.toml

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,14 @@ edition = "2021"
66
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
77

88
[dependencies]
9-
frender = { path = "../../packages/frender", features = [
9+
frender = { path = "../../packages/frender", default-features = false, features = [
1010
"web",
1111
"spawn",
12+
"html-components",
1213
"KeyedElements",
14+
"either",
1315
] }
1416
hooks = "3.0.0-alpha"
1517
gloo = "0.8.0"
1618
either = "1.8.1"
19+
console_error_panic_hook = "0.1.7"

examples/keyed-elements/src/main.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,8 @@ impl Data {
7171

7272
#[component(main(get_dom_element = "frender-root"))]
7373
fn Main() {
74+
console_error_panic_hook::set_once();
75+
7476
// data's updates are not reactive here
7577
let data = hooks::use_mut_with(|| hooks::GenSignalHook::new(Data::new())).to_signal();
7678

packages/frender-csr-ext/src/into_render_element_ext.rs

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -34,12 +34,8 @@ pub trait IntoRenderElementExt: ProvideRenderContext {
3434
Self::Renderer: RenderHtml,
3535
{
3636
async move {
37-
let (state, init) = self.provide_render_context(|render_context| {
38-
element.pinned_render_init(render_context)
39-
});
40-
37+
let (state, init) = element.pinned_render_init(self.renderer_mut());
4138
let mut state = pin!(state);
42-
4339
let mut ui_handle = self.provide_render_context(|render_context| {
4440
init.render_init_pinned(render_context, state.as_mut())
4541
});

packages/frender-csr-ext/src/render_element.rs

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -149,20 +149,20 @@ where
149149
return Poll::Ready(());
150150
}
151151

152-
let (mut state, ui_handle) = this.element_or_state.as_mut().as_pin_mut_state_or_insert(
153-
|element| {
154-
let (state, init) = this.p.provide_render_context(|render_context| {
155-
element.pinned_render_init(render_context)
156-
});
157-
(state, (init, &mut this.p))
158-
},
159-
|(init, this_p), state| {
160-
use frender_html::experimental::RenderInitPinned as _;
161-
this_p.provide_render_context(|render_context| {
152+
let (mut state, ui_handle) = this.p.provide_render_context(|render_context| {
153+
this.element_or_state.as_mut().as_pin_mut_state_or_insert(
154+
|element| {
155+
use frender_html::dom::render::RenderContext as _;
156+
let (state, init) = element.pinned_render_init(render_context.renderer_mut());
157+
(state, (init, render_context))
158+
},
159+
|(init, render_context), state| {
160+
use frender_html::experimental::RenderInitPinned as _;
161+
162162
init.render_init_pinned(render_context, state)
163-
})
164-
},
165-
);
163+
},
164+
)
165+
});
166166

167167
if let Poll::Pending = <E::RenderStateKind>::pinned_poll_render(
168168
this.p.renderer_mut(),

packages/frender-csr-web/src/renderer.rs

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -79,8 +79,6 @@ impl RenderWithContext for Renderer {
7979
}
8080

8181
impl RenderHtml for Renderer {
82-
type Text = Node<web_sys::Text>;
83-
8482
html_elements!(
8583
abbr: HtmlElement,
8684
address: HtmlElement,
Lines changed: 83 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use frender_html::{dom::csr::web::Node, dom::render::RenderTextFrom};
1+
use frender_html::dom::{csr::web::Node, render::RenderTextFrom};
22

33
use super::Renderer;
44

@@ -31,16 +31,16 @@ mod js_shims {
3131
}
3232
}
3333

34-
mod to_js_string {
34+
mod into_js_string {
3535
use wasm_bindgen::JsValue;
3636

37-
pub(super) trait ToJsString {
38-
fn to_js_string(&self) -> js_sys::JsString;
37+
pub(super) trait IntoJsString {
38+
fn into_js_string(self) -> js_sys::JsString;
3939
}
4040

41-
impl ToJsString for char {
42-
fn to_js_string(&self) -> js_sys::JsString {
43-
From::from(*self)
41+
impl IntoJsString for char {
42+
fn into_js_string(self) -> js_sys::JsString {
43+
From::from(self)
4444
}
4545
}
4646

@@ -56,88 +56,124 @@ mod to_js_string {
5656
}
5757

5858
impl_for_each_of!(
59-
impl<__> ToJsString
59+
impl<__> IntoJsString
6060
for each_of! {
6161
i8, u8, i16, u16, i32, u32, i64, u64, i128, u128, isize, usize,
6262
f32, f64,
63-
bool,
6463
}
6564
{
66-
fn to_js_string(&self) -> js_sys::JsString {
67-
super::js_shims::js_string(JsValue::from(*self))
65+
fn into_js_string(self) -> js_sys::JsString {
66+
super::js_shims::js_string(JsValue::from(self))
6867
}
6968
}
7069
);
7170
}
7271

73-
mod to_text_node {
72+
mod into_text_node {
73+
use std::{borrow::Cow, rc::Rc, sync::Arc};
74+
75+
use wasm_bindgen::JsCast as _;
76+
77+
use frender_common::{
78+
impl_many, strings::AsRefStr as _, value_kind::StaticRefOrTempOwned, TempStr,
79+
};
7480
use frender_html::dom::string_element::StringElement;
7581

7682
use super::Renderer;
7783

78-
pub(super) trait ToTextNode {
79-
fn to_text_node(&self, renderer: &mut Renderer) -> web_sys::Text;
84+
pub(super) trait IntoTextNode {
85+
fn into_text_node(self, renderer: &mut Renderer) -> web_sys::Text;
8086

81-
fn update_text_node(&self, renderer: &mut Renderer, text: &web_sys::Text);
87+
fn update_text_node(self, renderer: &mut Renderer, text: &web_sys::Text);
8288
}
8389

84-
impl<V: ?Sized + super::to_js_string::ToJsString> ToTextNode for V {
85-
fn to_text_node(&self, renderer: &mut Renderer) -> web_sys::Text {
86-
use wasm_bindgen::JsCast;
90+
impl<V: super::into_js_string::IntoJsString> IntoTextNode for V {
91+
fn into_text_node(self, renderer: &mut Renderer) -> web_sys::Text {
8792
super::js_shims::Document::create_text_node(
8893
renderer.document.unchecked_ref(),
89-
self.to_js_string(),
94+
self.into_js_string(),
9095
)
9196
}
9297

93-
fn update_text_node(&self, _: &mut Renderer, text: &web_sys::Text) {
94-
use wasm_bindgen::JsCast;
95-
super::js_shims::Text::set_data(text.unchecked_ref(), self.to_js_string())
98+
fn update_text_node(self, _: &mut Renderer, text: &web_sys::Text) {
99+
super::js_shims::Text::set_data(text.unchecked_ref(), self.into_js_string())
96100
}
97101
}
98102

99-
impl ToTextNode for str {
100-
fn to_text_node(&self, renderer: &mut Renderer) -> web_sys::Text {
101-
renderer.document.create_text_node(self)
103+
impl_many!(
104+
impl<__> IntoTextNode
105+
for each_of![
106+
//
107+
&'static str,
108+
String,
109+
Cow<'static, str>,
110+
TempStr<&str>,
111+
StaticRefOrTempOwned<'_, str>,
112+
Rc<str>,
113+
&Rc<str>,
114+
Arc<str>,
115+
&Arc<str>,
116+
]
117+
{
118+
fn into_text_node(self, renderer: &mut Renderer) -> web_sys::Text {
119+
renderer.document.create_text_node(self.as_ref_str())
120+
}
121+
122+
fn update_text_node(self, _: &mut Renderer, text: &web_sys::Text) {
123+
text.set_data(self.as_ref_str())
124+
}
125+
}
126+
);
127+
128+
impl IntoTextNode for StringElement {
129+
fn into_text_node(self, renderer: &mut Renderer) -> web_sys::Text {
130+
match self.into_js_string() {
131+
Ok(this) => super::js_shims::Document::create_text_node(
132+
renderer.document.unchecked_ref(),
133+
this,
134+
),
135+
Err(this) => renderer.document.create_text_node(&this),
136+
}
102137
}
103138

104-
fn update_text_node(&self, _: &mut Renderer, text: &web_sys::Text) {
105-
text.set_data(self)
139+
fn update_text_node(self, _: &mut Renderer, text: &web_sys::Text) {
140+
match self.into_js_string() {
141+
Ok(this) => super::js_shims::Text::set_data(text.unchecked_ref(), this),
142+
Err(this) => text.set_data(&this),
143+
}
106144
}
107145
}
108146

109-
impl ToTextNode for StringElement {
110-
fn to_text_node(&self, renderer: &mut Renderer) -> web_sys::Text {
147+
impl IntoTextNode for &StringElement {
148+
fn into_text_node(self, renderer: &mut Renderer) -> web_sys::Text {
111149
match self.as_js_string() {
112-
Ok(this) => {
113-
use wasm_bindgen::JsCast;
114-
super::js_shims::Document::create_text_node_ref(
115-
renderer.document.unchecked_ref(),
116-
this,
117-
)
118-
}
119-
Err(this) => renderer.document.create_text_node(this),
150+
Ok(this) => super::js_shims::Document::create_text_node_ref(
151+
renderer.document.unchecked_ref(),
152+
this,
153+
),
154+
Err(this) => renderer.document.create_text_node(&this),
120155
}
121156
}
122157

123-
fn update_text_node(&self, _: &mut Renderer, text: &web_sys::Text) {
158+
fn update_text_node(self, _: &mut Renderer, text: &web_sys::Text) {
124159
match self.as_js_string() {
125-
Ok(this) => {
126-
use wasm_bindgen::JsCast;
127-
super::js_shims::Text::set_data_ref(text.unchecked_ref(), this)
128-
}
129-
Err(this) => text.set_data(this),
160+
Ok(this) => super::js_shims::Text::set_data_ref(text.unchecked_ref(), this),
161+
Err(this) => text.set_data(&this),
130162
}
131163
}
132164
}
133165
}
134166

135-
impl<V: ?Sized + to_text_node::ToTextNode> RenderTextFrom<Node<web_sys::Text>, V> for Renderer {
136-
fn render_text_from(&mut self, v: &V) -> Node<web_sys::Text> {
137-
Node(v.to_text_node(self))
167+
impl<V: into_text_node::IntoTextNode> RenderTextFrom<V> for Renderer {
168+
type Text = Node<web_sys::Text>;
169+
170+
fn render_text_from(render_context: &mut Self::RenderContext<'_>, v: V) -> Self::Text {
171+
let text = v.into_text_node(render_context.renderer);
172+
<Self as frender_html::dom::csr::web::Renderer>::mount_node(render_context, &text);
173+
Node(text)
138174
}
139175

140-
fn update_text_from(&mut self, text: &mut Node<web_sys::Text>, v: &V) {
176+
fn update_text_from(&mut self, text: &mut Self::Text, v: V) {
141177
v.update_text_node(self, &text.0)
142178
}
143179
}

packages/frender-csr/src/render.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,4 +20,9 @@ pub trait RenderContext {
2020
&mut self,
2121
f: impl FnOnce(&mut <Self::Renderer as RenderWithContext>::RenderContext<'_>) -> Res,
2222
) -> Res;
23+
24+
fn map_mut_unrendered_render_context_and_then_reposition<Res>(
25+
&mut self,
26+
f: impl FnOnce(&mut <Self::Renderer as RenderWithContext>::RenderContext<'_>) -> Res,
27+
) -> Res;
2328
}

packages/frender-dom/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ features = [
3131
"Event",
3232
"EventTarget",
3333
"Document",
34+
"DocumentFragment",
3435
"Node",
3536
"Element",
3637
"HtmlElement",

packages/frender-dom/src/csr/web.rs

Lines changed: 39 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ use wasm_bindgen::UnwrapThrowExt as _;
88

99
use crate::{
1010
render::RenderWithContext,
11-
ui_handle::{UiHandle, UnmountedUiHandle},
11+
ui_handle::{ProvideMutMounted, UiHandle, UnmountedUiHandle},
1212
};
1313

1414
pub mod event_listener;
@@ -35,6 +35,16 @@ impl<N: AsRef<web_sys::Node>, R: ?Sized + Renderer> UnmountedUiHandle<R> for Nod
3535
}
3636
}
3737

38+
impl<N: AsRef<web_sys::Node>, R: ?Sized + Renderer> ProvideMutMounted<R> for Node<N> {
39+
fn provide_mut_mounted<Out>(
40+
&mut self,
41+
renderer: &mut R,
42+
f: impl FnOnce(&mut R, &mut Self::Mounted) -> Out,
43+
) -> Out {
44+
f(renderer, self)
45+
}
46+
}
47+
3848
impl<N: AsRef<web_sys::Node>, R: ?Sized + Renderer> UiHandle<R> for Node<N> {
3949
type Unmounted = Node<N>;
4050

@@ -189,8 +199,15 @@ impl<'a> Cursor<'a> {
189199
}
190200

191201
pub fn force_add_node(&mut self, node: &web_sys::Node) {
202+
if self.skipped {
203+
const MSG: &str = "Dom renderer's cursor can not be skipped when moving or adding node";
204+
web_sys::console::warn_1(&MSG.into());
205+
panic!("{}", MSG)
206+
}
207+
192208
match &self.position {
193209
CursorPosition::FirstChildOf(parent) => {
210+
// TODO: is this correct for DocumentFragment?
194211
parent.prepend_with_node_1(node).unwrap_throw();
195212
}
196213
CursorPosition::After(pre) => {
@@ -297,6 +314,27 @@ impl<'a, R: ?Sized + Renderer> crate::render::RenderContext for RenderContext<'a
297314
fn mark_cursor_skipped(&mut self) {
298315
self.cursor.skipped = true;
299316
}
317+
318+
fn map_mut_unrendered_render_context_and_then_reposition<Res>(
319+
&mut self,
320+
f: impl FnOnce(&mut <Self::Renderer as RenderWithContext>::RenderContext<'_>) -> Res,
321+
) -> Res {
322+
use wasm_bindgen::JsCast as _;
323+
let fragment = self.renderer.document().create_document_fragment();
324+
325+
let mut cursor = Cursor {
326+
position: CursorPosition::FirstChildOf(Cow::Borrowed(fragment.unchecked_ref())), // TODO: type checking
327+
skipped: false,
328+
};
329+
let res = f(&mut RenderContext {
330+
renderer: self.renderer,
331+
cursor: &mut cursor,
332+
});
333+
334+
self.cursor.force_add_node(&fragment);
335+
336+
res
337+
}
300338
}
301339

302340
impl<

0 commit comments

Comments
 (0)