feat: filter UI
This commit is contained in:
parent
3740224f79
commit
ed9eba8dc1
@ -45,6 +45,7 @@ use crate::server_functions;
|
||||
use leptos::Params;
|
||||
use leptos::prelude::*;
|
||||
use leptos_router::params::Params;
|
||||
use std::collections::BTreeSet;
|
||||
use web_sys::MouseEvent;
|
||||
|
||||
#[derive(Params, PartialEq, Clone)]
|
||||
@ -122,6 +123,19 @@ fn WithWall(#[prop(into)] wall: Signal<models::Wall>) -> impl IntoView {
|
||||
|
||||
let (problem_uid, set_problem_uid) = leptos_router::hooks::query_signal::<models::ProblemUid>("problem");
|
||||
|
||||
// Filter
|
||||
let (filter_holds, set_filter_holds) = signal(BTreeSet::new());
|
||||
let filter_add_hold = move |hold_pos: models::HoldPosition| {
|
||||
set_filter_holds.update(move |set| {
|
||||
set.insert(hold_pos);
|
||||
});
|
||||
};
|
||||
let filter_remove_hold = move |hold_pos: models::HoldPosition| {
|
||||
set_filter_holds.update(move |set| {
|
||||
set.remove(&hold_pos);
|
||||
});
|
||||
};
|
||||
|
||||
let problem = crate::resources::problem_by_uid_optional(wall_uid, problem_uid.into());
|
||||
let user_interaction = crate::resources::user_interaction(wall_uid, problem_uid.into());
|
||||
|
||||
@ -140,6 +154,14 @@ fn WithWall(#[prop(into)] wall: Signal<models::Wall>) -> impl IntoView {
|
||||
// merge outer option (resource hasn't resolved yet) with inner option (there is no problem for the wall)
|
||||
let problem_signal = Signal::derive(move || problem.get().transpose().map(Option::flatten));
|
||||
|
||||
let on_click_hold = move |hold_position: models::HoldPosition| {
|
||||
set_filter_holds.update(|set| {
|
||||
if !set.remove(&hold_position) {
|
||||
set.insert(hold_position);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
let grid = {
|
||||
let wall = wall.clone();
|
||||
view! {
|
||||
@ -151,7 +173,9 @@ fn WithWall(#[prop(into)] wall: Signal<models::Wall>) -> impl IntoView {
|
||||
Suspend::new(async move {
|
||||
let wall = wall.clone();
|
||||
tracing::info!("executing grid suspend");
|
||||
let view = view! { <Grid wall=wall.get() problem=problem_signal /> };
|
||||
let view = view! {
|
||||
<Grid wall=wall.get() problem=problem_signal on_click_hold />
|
||||
};
|
||||
Ok::<_, ServerFnError>(view)
|
||||
})
|
||||
}
|
||||
@ -160,12 +184,25 @@ fn WithWall(#[prop(into)] wall: Signal<models::Wall>) -> impl IntoView {
|
||||
}
|
||||
};
|
||||
|
||||
let filter = move || {
|
||||
let mut cells = vec![];
|
||||
for hold_pos in filter_holds.get() {
|
||||
let w = &*wall.read();
|
||||
if let Some(hold) = w.holds.get(&hold_pos).cloned() {
|
||||
let v = view! { <Hold hold /> };
|
||||
cells.push(v);
|
||||
}
|
||||
}
|
||||
|
||||
view! { <div class="grid grid-cols-4 gap-2">{cells}</div> }
|
||||
};
|
||||
|
||||
view! {
|
||||
<div class="inline-grid grid-cols-1 gap-8 md:grid-cols-[auto,1fr]">
|
||||
<div>{grid}</div>
|
||||
|
||||
<div class="flex flex-col">
|
||||
<Section title="Filter">{}</Section>
|
||||
<div class="flex flex-col max-w-screen-sm">
|
||||
<Section title="Filter">{filter}</Section>
|
||||
|
||||
<Separator />
|
||||
|
||||
@ -369,14 +406,27 @@ fn AttemptRadio(
|
||||
|
||||
#[component]
|
||||
#[tracing::instrument(skip_all)]
|
||||
fn Grid(wall: models::Wall, #[prop(into)] problem: Signal<Result<Option<models::Problem>, ServerFnError>>) -> impl IntoView {
|
||||
fn Grid(
|
||||
wall: models::Wall,
|
||||
#[prop(into)] problem: Signal<Result<Option<models::Problem>, ServerFnError>>,
|
||||
on_click_hold: impl Fn(models::HoldPosition) + 'static,
|
||||
) -> impl IntoView {
|
||||
tracing::debug!("Enter");
|
||||
|
||||
let on_click_hold = std::rc::Rc::new(on_click_hold);
|
||||
|
||||
let mut cells = vec![];
|
||||
for (&hold_position, hold) in &wall.holds {
|
||||
let role = move || problem.get().map(|o| o.and_then(|p| p.holds.get(&hold_position).copied()));
|
||||
let role = Signal::derive(role);
|
||||
let cell = view! { <Hold role hold=hold.clone() /> };
|
||||
|
||||
let on_click = {
|
||||
let on_click_hold = std::rc::Rc::clone(&on_click_hold);
|
||||
move |_| {
|
||||
on_click_hold(hold_position);
|
||||
}
|
||||
};
|
||||
let cell = view! { <Hold on:click=on_click role hold=hold.clone() /> };
|
||||
cells.push(cell);
|
||||
}
|
||||
let grid_classes = format!("grid grid-rows-{} grid-cols-{} gap-1", wall.rows, wall.cols,);
|
||||
@ -393,13 +443,20 @@ fn Grid(wall: models::Wall, #[prop(into)] problem: Signal<Result<Option<models::
|
||||
// TODO: refactor this to use the Problem component
|
||||
#[component]
|
||||
#[tracing::instrument(skip_all)]
|
||||
fn Hold(hold: models::Hold, role: Signal<Result<Option<HoldRole>, ServerFnError>>) -> impl IntoView {
|
||||
fn Hold(
|
||||
hold: models::Hold,
|
||||
|
||||
#[prop(optional)]
|
||||
#[prop(into)]
|
||||
role: Option<Signal<Result<Option<HoldRole>, ServerFnError>>>,
|
||||
) -> impl IntoView {
|
||||
tracing::trace!("Enter");
|
||||
|
||||
move || {
|
||||
let mut class = "bg-sky-100 aspect-square rounded hover:brightness-125".to_string();
|
||||
if let Some(role) = role {
|
||||
let role = role.get()?;
|
||||
|
||||
let class = {
|
||||
let role_classes = match role {
|
||||
Some(HoldRole::Start) => Some("outline outline-offset-2 outline-green-500"),
|
||||
Some(HoldRole::Normal) => Some("outline outline-offset-2 outline-blue-500"),
|
||||
@ -407,13 +464,11 @@ fn Hold(hold: models::Hold, role: Signal<Result<Option<HoldRole>, ServerFnError>
|
||||
Some(HoldRole::End) => Some("outline outline-offset-2 outline-red-500"),
|
||||
None => Some("brightness-50"),
|
||||
};
|
||||
let mut s = "bg-sky-100 aspect-square rounded hover:brightness-125".to_string();
|
||||
if let Some(c) = role_classes {
|
||||
s.push(' ');
|
||||
s.push_str(c);
|
||||
class.push(' ');
|
||||
class.push_str(c);
|
||||
}
|
||||
}
|
||||
s
|
||||
};
|
||||
|
||||
let img = hold.image.as_ref().map(|img| {
|
||||
let srcset = img.srcset();
|
||||
|
Loading…
x
Reference in New Issue
Block a user