feat(kanban): prove accessible local reorder

req: client_local/013\nreq: accessibility/002\nreq: accessibility/003\nreq: accessibility/004\nreq: accessibility/006\nreq: milestone/001
This commit is contained in:
slhx agent
2026-07-13 14:29:21 +02:00
parent dd5ea0827f
commit 7eb65d77c8
11 changed files with 407 additions and 48 deletions
+24 -5
View File
@@ -4,16 +4,35 @@ version.workspace = true
edition.workspace = true
publish = false
[features]
default = ["server"]
server = ["dep:axum", "dep:futures-util", "dep:hemx-axum", "dep:tokio"]
client = ["hemx/client"]
fixture = []
[lib]
path = "src/lib.rs"
crate-type = ["cdylib", "rlib"]
[[bin]]
name = "hemx-kanban-example"
path = "src/main.rs"
required-features = ["server"]
[[bin]]
name = "client-fixture"
path = "src/bin/client_fixture.rs"
required-features = ["fixture"]
[dependencies]
axum = "0.8"
futures-util = "0.3"
hemplate = { path = "../../../hemplate/hemplate" }
axum = { version = "0.8", optional = true }
futures-util = { version = "0.3", optional = true }
hemx = { path = "../../hemx" }
hemx-axum = { path = "../../hemx-axum" }
tokio = { version = "1", features = ["macros", "net", "rt-multi-thread", "time"] }
hemx-axum = { path = "../../hemx-axum", optional = true }
tokio = { version = "1", features = ["macros", "net", "rt-multi-thread", "time"], optional = true }
[target.'cfg(not(target_arch = "wasm32"))'.dependencies]
hemplate = { path = "../../../hemplate/hemplate" }
[dev-dependencies]
scraper = "0.23"
@@ -0,0 +1,3 @@
fn main() {
print!("{}", hemx_kanban_example::render_client_fixture());
}
+60
View File
@@ -1,6 +1,66 @@
#[hemx::surface]
pub mod ui {}
#[cfg(feature = "client")]
#[hemx::handler(client)]
pub fn reorder_card(
event: hemx::wasm::ClientEvent,
state: hemx::wasm::ClientState,
) -> impl hemx::IntoEffect {
let card = event.value.unwrap_or_else(|| "1".to_owned());
let order = state.encoded.split('|').collect::<Vec<_>>();
let move_effect = if card == order.first().copied().unwrap_or("1") {
ui::client_board::client_cards.move_to_end(card.clone())
} else {
ui::client_board::client_cards.move_before(
card.clone(),
order.first().copied().unwrap_or("1").to_owned(),
)
};
vec![
move_effect,
ui::client_board::client_notice.text(format!("Moved {card} with {}", event.kind)),
]
}
#[cfg(all(feature = "fixture", not(target_arch = "wasm32")))]
mod fixture {
use super::ui;
use hemplate::Hemplate;
use hemx::Html;
#[derive(Hemplate)]
struct ClientBoard {
cards: Vec<ClientCard>,
}
#[derive(Hemplate)]
struct ClientCard {
id: u64,
title: &'static str,
}
pub fn render() -> Html {
ui::client_board::page(&ClientBoard {
cards: vec![
ClientCard {
id: 1,
title: "First",
},
ClientCard {
id: 2,
title: "Second",
},
],
})
}
}
#[cfg(all(feature = "fixture", not(target_arch = "wasm32")))]
pub fn render_client_fixture() -> hemx::Html {
fixture::render()
}
#[cfg(test)]
mod tests {
use super::ui::{board, board_card};
+1 -1
View File
@@ -6,7 +6,7 @@
<select name="column" required="required">{+= self.options =+}</select>
<button type="submit">Add card</button>
</form>
<p data-hemx-slot="notice">Ready</p>
<p id="kanban-status" data-hemx-slot="notice" role="status" aria-live="polite">Ready</p>
</header>
<div data-hemx-slot="board">{+= self.board =+}</div>
@@ -0,0 +1,9 @@
<section data-hemx-root="kanban_client" data-hemx-st="1|2" data-hemx-client-state-version="1" data-hemx-client-module="/kanban_client.js">
<p id="kanban-status" data-hemx-slot="client_notice" role="status" aria-live="polite">Ready</p>
<ul data-hemx-slot="client_cards">
<template h-for="card in &self.cards" h-key="card.id">
{+ card +}
</template>
</ul>
<div data-hemx-handle="reorder_card" data-hemx-on="drop" data-hemx-client="reorder_card" data-hemx-client-event="drop" data-hemx-client-policy="latest">Drop card</div>
</section>
@@ -0,0 +1,4 @@
<li class="card" +data-key="self.id" draggable="true" data-hemx-handle="client_card" data-hemx-on="dragstart">
<span>{+ self.title +}</span>
<button type="button" data-hemx-handle="client_move_right" data-hemx-on="click keydown" data-hemx-client="reorder_card" data-hemx-client-event="click keydown" data-hemx-client-policy="latest" +data-card-id="self.id" aria-describedby="kanban-status">Move right</button>
</li>