refactor(kanban): scope card handles to partials
Move kanban card actions onto the reusable card partial, use generated render helpers for board/page composition, and remove manual handle fields plus hidden shell-level action anchors from the composed path. req: component/003 req: view/001 req: examples/001
This commit is contained in:
@@ -69,7 +69,7 @@ mod tests {
|
||||
// req: examples/001 req: form/002 req: codegen/003
|
||||
#[test]
|
||||
fn kanban_template_exports_form_and_card_handles() {
|
||||
assert_ne!(ui::board::handles::create_card.id(), ui::board::handles::move_right.id());
|
||||
assert_ne!(ui::board::handles::create_card.id(), ui::board_card::handles::move_right.id());
|
||||
assert_eq!(ui::board::forms::create_card.field("title").resource, ui::board::forms::create_card.id());
|
||||
}
|
||||
}
|
||||
|
||||
+18
-29
@@ -4,9 +4,11 @@ use axum::routing::get;
|
||||
use axum::Router;
|
||||
use futures_util::{stream, StreamExt};
|
||||
use hemplate::Hemplate;
|
||||
use slhx::{Handle, IntoEffect, RenderSlotExt, SafeHtml};
|
||||
use slhx::{IntoEffect, RenderSlotExt, SafeHtml};
|
||||
use slhx_axum::{runtime_js, sse, DispatchRejection, EffectResponse, HandlerRegistry, InteractionForm, PageRequest};
|
||||
use slhx_kanban_example::ui;
|
||||
use slhx_kanban_example::ui::board::{forms, handles, slots};
|
||||
use slhx_kanban_example::ui::board_card::handles as card_handles;
|
||||
use std::collections::BTreeMap;
|
||||
use std::convert::Infallible;
|
||||
use std::net::SocketAddr;
|
||||
@@ -51,9 +53,6 @@ struct BoardColumn {
|
||||
struct BoardCard {
|
||||
id: u64,
|
||||
title: String,
|
||||
left_handle: Handle<()>,
|
||||
right_handle: Handle<()>,
|
||||
delete_handle: Handle<()>,
|
||||
left_disabled: bool,
|
||||
right_disabled: bool,
|
||||
}
|
||||
@@ -131,13 +130,13 @@ async fn interact(
|
||||
// req: push/001 req: push/003 req: examples/001
|
||||
async fn events(Query(params): Query<BTreeMap<String, String>>) -> impl IntoResponse {
|
||||
if params.contains_key("once") {
|
||||
let effect = ui::board::slots::presence.render(&Presence { count: 1 });
|
||||
let effect = slots::presence.render(&Presence { count: 1 });
|
||||
return sse(stream::iter([Ok::<_, Infallible>(effect.into_batch(ui::BUILD_FINGERPRINT))]).boxed());
|
||||
}
|
||||
|
||||
let batches = stream::unfold(1_u64, |count| async move {
|
||||
tokio::time::sleep(Duration::from_secs(4)).await;
|
||||
let effect = ui::board::slots::presence.render(&Presence { count });
|
||||
let effect = slots::presence.render(&Presence { count });
|
||||
Some((Ok::<_, Infallible>(effect.into_batch(ui::BUILD_FINGERPRINT)), count + 1))
|
||||
})
|
||||
.boxed();
|
||||
@@ -146,7 +145,7 @@ async fn events(Query(params): Query<BTreeMap<String, String>>) -> impl IntoResp
|
||||
|
||||
fn registry(state: Arc<AppState>) -> HandlerRegistry {
|
||||
HandlerRegistry::new(ui::BUILD_FINGERPRINT)
|
||||
.register_handle(ui::board::handles::create_card, {
|
||||
.register_handle(handles::create_card, {
|
||||
let state = state.clone();
|
||||
move |form| {
|
||||
// req: examples/001 req: form/002
|
||||
@@ -161,7 +160,7 @@ fn registry(state: Arc<AppState>) -> HandlerRegistry {
|
||||
board_effects(&board, "Card added")
|
||||
}
|
||||
})
|
||||
.register_handle(ui::board::handles::move_left, {
|
||||
.register_handle(card_handles::move_left, {
|
||||
let state = state.clone();
|
||||
move |form| {
|
||||
// req: examples/001 req: list/003
|
||||
@@ -172,7 +171,7 @@ fn registry(state: Arc<AppState>) -> HandlerRegistry {
|
||||
board_effects(&board, if moved { "Card moved left" } else { "Card not found" })
|
||||
}
|
||||
})
|
||||
.register_handle(ui::board::handles::move_right, {
|
||||
.register_handle(card_handles::move_right, {
|
||||
let state = state.clone();
|
||||
move |form| {
|
||||
// req: examples/001 req: list/003
|
||||
@@ -183,7 +182,7 @@ fn registry(state: Arc<AppState>) -> HandlerRegistry {
|
||||
board_effects(&board, if moved { "Card moved right" } else { "Card not found" })
|
||||
}
|
||||
})
|
||||
.register_handle(ui::board::handles::delete_card, {
|
||||
.register_handle(card_handles::delete_card, {
|
||||
let state = state.clone();
|
||||
move |form| {
|
||||
// req: examples/001 req: list/003
|
||||
@@ -199,9 +198,9 @@ fn registry(state: Arc<AppState>) -> HandlerRegistry {
|
||||
|
||||
fn board_effects(board: &BoardState, notice: &'static str) -> impl IntoEffect {
|
||||
(
|
||||
ui::board::slots::board.render(&board_view(board)),
|
||||
ui::board::slots::notice.text(notice),
|
||||
ui::board::forms::create_card.clear("title"),
|
||||
slots::board.html(render_board(board)),
|
||||
slots::notice.text(notice),
|
||||
forms::create_card.clear("title"),
|
||||
)
|
||||
}
|
||||
|
||||
@@ -227,10 +226,11 @@ fn parse_column(value: Option<&str>) -> usize {
|
||||
fn page_html(board: &BoardState) -> String {
|
||||
// Explicit full-page composition boundary for already-rendered hemplate fragments.
|
||||
// req: html_safety/002 req: view/001
|
||||
ui::board::lower_html(render_template(&Board {
|
||||
ui::board::render_html(&Board {
|
||||
options: render_options(),
|
||||
board: render_board(board),
|
||||
}))
|
||||
})
|
||||
.into_string()
|
||||
}
|
||||
|
||||
fn shell(body: String) -> String {
|
||||
@@ -262,7 +262,7 @@ fn shell(body: String) -> String {
|
||||
|
||||
fn render_options() -> SafeHtml {
|
||||
// req: html_safety/002 req: view/001
|
||||
slhx::render_html(&ColumnOptions {
|
||||
ui::render_html(&ColumnOptions {
|
||||
options: COLUMNS
|
||||
.iter()
|
||||
.map(|(id, title)| ColumnOption { id, title })
|
||||
@@ -291,29 +291,18 @@ fn board_view(board: &BoardState) -> BoardColumns {
|
||||
|
||||
fn render_board(board: &BoardState) -> SafeHtml {
|
||||
// req: html_safety/002 req: view/001
|
||||
slhx::render_html(&board_view(board))
|
||||
ui::render_html(&board_view(board))
|
||||
}
|
||||
|
||||
fn render_card(card: &Card) -> BoardCard {
|
||||
BoardCard {
|
||||
id: card.id,
|
||||
title: card.title.clone(),
|
||||
left_handle: ui::board::handles::move_left,
|
||||
right_handle: ui::board::handles::move_right,
|
||||
delete_handle: ui::board::handles::delete_card,
|
||||
left_disabled: card.column == 0,
|
||||
right_disabled: card.column + 1 == COLUMNS.len(),
|
||||
}
|
||||
}
|
||||
|
||||
fn render_template(template: &impl Hemplate) -> String {
|
||||
let mut html = String::new();
|
||||
template
|
||||
.render_into(&mut html)
|
||||
.expect("kanban hemplate view renders");
|
||||
html
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
@@ -334,7 +323,7 @@ mod tests {
|
||||
assert_eq!(document.select(&selector("section[data-slhx-root=\"kanban\"]")).count(), 1);
|
||||
assert_eq!(document.select(&selector("select[name=\"column\"] > option")).count(), 3);
|
||||
assert_eq!(document.select(&selector("[data-sid]")).count(), 3);
|
||||
assert_eq!(document.select(&selector("[data-hid]")).count(), 4);
|
||||
assert_eq!(document.select(&selector("[data-hid]")).count(), 1);
|
||||
}
|
||||
|
||||
// req: html_safety/002 req: view/001 req: test/005
|
||||
|
||||
Reference in New Issue
Block a user