feat(slhx): render hemplate views into slots
Add render_html plus slot render_view helpers that call hemplate render_into, and migrate a kanban effect path from explicit SafeHtml/html handling to the renderable-view helper. req: view/001 req: html_safety/002
This commit is contained in:
Generated
+1
@@ -1360,6 +1360,7 @@ checksum = "0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5"
|
|||||||
name = "slhx"
|
name = "slhx"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"hemplate",
|
||||||
"slhx-core",
|
"slhx-core",
|
||||||
"slhx-derive",
|
"slhx-derive",
|
||||||
]
|
]
|
||||||
|
|||||||
+13
-13
@@ -4,7 +4,7 @@ use axum::routing::get;
|
|||||||
use axum::Router;
|
use axum::Router;
|
||||||
use futures_util::{stream, StreamExt};
|
use futures_util::{stream, StreamExt};
|
||||||
use hemplate::Hemplate;
|
use hemplate::Hemplate;
|
||||||
use slhx::{Handle, IntoEffect, SafeHtml};
|
use slhx::{Handle, IntoEffect, RenderSlotExt, SafeHtml};
|
||||||
use slhx_axum::{runtime_js, sse, DispatchRejection, EffectResponse, HandlerRegistry, InteractionForm, PageRequest};
|
use slhx_axum::{runtime_js, sse, DispatchRejection, EffectResponse, HandlerRegistry, InteractionForm, PageRequest};
|
||||||
use slhx_kanban_example::ui;
|
use slhx_kanban_example::ui;
|
||||||
use std::collections::BTreeMap;
|
use std::collections::BTreeMap;
|
||||||
@@ -131,13 +131,13 @@ async fn interact(
|
|||||||
// req: push/001 req: push/003 req: examples/001
|
// req: push/001 req: push/003 req: examples/001
|
||||||
async fn events(Query(params): Query<BTreeMap<String, String>>) -> impl IntoResponse {
|
async fn events(Query(params): Query<BTreeMap<String, String>>) -> impl IntoResponse {
|
||||||
if params.contains_key("once") {
|
if params.contains_key("once") {
|
||||||
let effect = ui::board::slots::presence.html(render_presence(1));
|
let effect = ui::board::slots::presence.render_view(&Presence { count: 1 });
|
||||||
return sse(stream::iter([Ok::<_, Infallible>(effect.into_batch(ui::BUILD_FINGERPRINT))]).boxed());
|
return sse(stream::iter([Ok::<_, Infallible>(effect.into_batch(ui::BUILD_FINGERPRINT))]).boxed());
|
||||||
}
|
}
|
||||||
|
|
||||||
let batches = stream::unfold(1_u64, |count| async move {
|
let batches = stream::unfold(1_u64, |count| async move {
|
||||||
tokio::time::sleep(Duration::from_secs(4)).await;
|
tokio::time::sleep(Duration::from_secs(4)).await;
|
||||||
let effect = ui::board::slots::presence.html(render_presence(count));
|
let effect = ui::board::slots::presence.render_view(&Presence { count });
|
||||||
Some((Ok::<_, Infallible>(effect.into_batch(ui::BUILD_FINGERPRINT)), count + 1))
|
Some((Ok::<_, Infallible>(effect.into_batch(ui::BUILD_FINGERPRINT)), count + 1))
|
||||||
})
|
})
|
||||||
.boxed();
|
.boxed();
|
||||||
@@ -199,7 +199,7 @@ fn registry(state: Arc<AppState>) -> HandlerRegistry {
|
|||||||
|
|
||||||
fn board_effects(board: &BoardState, notice: &'static str) -> impl IntoEffect {
|
fn board_effects(board: &BoardState, notice: &'static str) -> impl IntoEffect {
|
||||||
(
|
(
|
||||||
ui::board::slots::board.html(render_board(board)),
|
ui::board::slots::board.render_view(&board_view(board)),
|
||||||
ui::board::slots::notice.text(notice),
|
ui::board::slots::notice.text(notice),
|
||||||
ui::board::forms::create_card.clear("title"),
|
ui::board::forms::create_card.clear("title"),
|
||||||
)
|
)
|
||||||
@@ -269,9 +269,9 @@ fn render_options() -> SafeHtml {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn render_board(board: &BoardState) -> SafeHtml {
|
fn board_view(board: &BoardState) -> BoardColumns {
|
||||||
// req: html_safety/002 req: view/001
|
// req: html_safety/002 req: view/001
|
||||||
render_html(&BoardColumns {
|
BoardColumns {
|
||||||
columns: COLUMNS
|
columns: COLUMNS
|
||||||
.iter()
|
.iter()
|
||||||
.enumerate()
|
.enumerate()
|
||||||
@@ -285,7 +285,12 @@ fn render_board(board: &BoardState) -> SafeHtml {
|
|||||||
.collect(),
|
.collect(),
|
||||||
})
|
})
|
||||||
.collect(),
|
.collect(),
|
||||||
})
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn render_board(board: &BoardState) -> SafeHtml {
|
||||||
|
// req: html_safety/002 req: view/001
|
||||||
|
render_html(&board_view(board))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn render_card(card: &Card) -> BoardCard {
|
fn render_card(card: &Card) -> BoardCard {
|
||||||
@@ -300,11 +305,6 @@ fn render_card(card: &Card) -> BoardCard {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn render_presence(count: u64) -> SafeHtml {
|
|
||||||
// req: html_safety/002 req: view/001
|
|
||||||
render_html(&Presence { count })
|
|
||||||
}
|
|
||||||
|
|
||||||
fn render_html(template: &impl Hemplate) -> SafeHtml {
|
fn render_html(template: &impl Hemplate) -> SafeHtml {
|
||||||
SafeHtml::trusted(render_template(template))
|
SafeHtml::trusted(render_template(template))
|
||||||
}
|
}
|
||||||
@@ -368,7 +368,7 @@ mod tests {
|
|||||||
// req: html_safety/002 req: view/001 req: test/005
|
// req: html_safety/002 req: view/001 req: test/005
|
||||||
#[test]
|
#[test]
|
||||||
fn presence_payload_is_rendered_by_a_hemplate_view() {
|
fn presence_payload_is_rendered_by_a_hemplate_view() {
|
||||||
let html = render_presence(7);
|
let html = render_html(&Presence { count: 7 });
|
||||||
let document = Html::parse_fragment(html.as_str());
|
let document = Html::parse_fragment(html.as_str());
|
||||||
assert_eq!(document.select(&selector("span.presence")).count(), 2);
|
assert_eq!(document.select(&selector("span.presence")).count(), 2);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
|
|||||||
@@ -7,5 +7,6 @@ edition.workspace = true
|
|||||||
path = "src/lib.rs"
|
path = "src/lib.rs"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
hemplate = { path = "../../hemplate/hemplate" }
|
||||||
slhx-core = { path = "../slhx-core" }
|
slhx-core = { path = "../slhx-core" }
|
||||||
slhx-derive = { path = "../slhx-derive" }
|
slhx-derive = { path = "../slhx-derive" }
|
||||||
|
|||||||
@@ -6,11 +6,52 @@
|
|||||||
pub use slhx_core::*;
|
pub use slhx_core::*;
|
||||||
pub use slhx_derive::{app, component, form, handler, surface};
|
pub use slhx_derive::{app, component, form, handler, surface};
|
||||||
|
|
||||||
|
pub fn render_html(view: &impl hemplate::Hemplate) -> SafeHtml {
|
||||||
|
let mut html = String::new();
|
||||||
|
view.render_into(&mut html)
|
||||||
|
.expect("hemplate view renders into slhx effect payload");
|
||||||
|
SafeHtml::trusted(html)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait RenderSlotExt {
|
||||||
|
fn render_view(self, view: &impl hemplate::Hemplate) -> Effect;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> RenderSlotExt for Slot<T> {
|
||||||
|
fn render_view(self, view: &impl hemplate::Hemplate) -> Effect {
|
||||||
|
self.html(render_html(view))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait RenderKeyedSlotExt<K> {
|
||||||
|
fn append_view(self, key: K, view: &impl hemplate::Hemplate) -> Effect;
|
||||||
|
fn prepend_view(self, key: K, view: &impl hemplate::Hemplate) -> Effect;
|
||||||
|
fn replace_view(self, key: K, view: &impl hemplate::Hemplate) -> Effect;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<K, T> RenderKeyedSlotExt<K> for KeyedSlot<K, T>
|
||||||
|
where
|
||||||
|
K: ToString,
|
||||||
|
{
|
||||||
|
fn append_view(self, key: K, view: &impl hemplate::Hemplate) -> Effect {
|
||||||
|
self.append_html(key, render_html(view))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn prepend_view(self, key: K, view: &impl hemplate::Hemplate) -> Effect {
|
||||||
|
self.prepend_html(key, render_html(view))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn replace_view(self, key: K, view: &impl hemplate::Hemplate) -> Effect {
|
||||||
|
self.replace_html(key, render_html(view))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub mod prelude {
|
pub mod prelude {
|
||||||
pub use slhx_core::{
|
pub use slhx_core::{
|
||||||
navigate, push, redirect, replace, Atom, BuildFingerprint, ComponentRef, CssClass,
|
navigate, push, redirect, replace, Atom, BuildFingerprint, ComponentRef, CssClass,
|
||||||
CssClasses, Effect, EventName, Form, FormModel, FormValue, Handle, IntoEffect, KeyedSlot,
|
CssClasses, Effect, EventName, Form, FormModel, FormValue, Handle, IntoEffect, KeyedSlot,
|
||||||
Slot,
|
Slot,
|
||||||
};
|
};
|
||||||
|
pub use crate::{render_html, RenderKeyedSlotExt, RenderSlotExt};
|
||||||
pub use slhx_derive::{app, component, form, handler, surface};
|
pub use slhx_derive::{app, component, form, handler, surface};
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user