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::Params;
|
||||||
use leptos::prelude::*;
|
use leptos::prelude::*;
|
||||||
use leptos_router::params::Params;
|
use leptos_router::params::Params;
|
||||||
|
use std::collections::BTreeSet;
|
||||||
use web_sys::MouseEvent;
|
use web_sys::MouseEvent;
|
||||||
|
|
||||||
#[derive(Params, PartialEq, Clone)]
|
#[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");
|
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 problem = crate::resources::problem_by_uid_optional(wall_uid, problem_uid.into());
|
||||||
let user_interaction = crate::resources::user_interaction(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)
|
// 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 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 grid = {
|
||||||
let wall = wall.clone();
|
let wall = wall.clone();
|
||||||
view! {
|
view! {
|
||||||
@ -151,7 +173,9 @@ fn WithWall(#[prop(into)] wall: Signal<models::Wall>) -> impl IntoView {
|
|||||||
Suspend::new(async move {
|
Suspend::new(async move {
|
||||||
let wall = wall.clone();
|
let wall = wall.clone();
|
||||||
tracing::info!("executing grid suspend");
|
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)
|
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! {
|
view! {
|
||||||
<div class="inline-grid grid-cols-1 gap-8 md:grid-cols-[auto,1fr]">
|
<div class="inline-grid grid-cols-1 gap-8 md:grid-cols-[auto,1fr]">
|
||||||
<div>{grid}</div>
|
<div>{grid}</div>
|
||||||
|
|
||||||
<div class="flex flex-col">
|
<div class="flex flex-col max-w-screen-sm">
|
||||||
<Section title="Filter">{}</Section>
|
<Section title="Filter">{filter}</Section>
|
||||||
|
|
||||||
<Separator />
|
<Separator />
|
||||||
|
|
||||||
@ -369,14 +406,27 @@ fn AttemptRadio(
|
|||||||
|
|
||||||
#[component]
|
#[component]
|
||||||
#[tracing::instrument(skip_all)]
|
#[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");
|
tracing::debug!("Enter");
|
||||||
|
|
||||||
|
let on_click_hold = std::rc::Rc::new(on_click_hold);
|
||||||
|
|
||||||
let mut cells = vec![];
|
let mut cells = vec![];
|
||||||
for (&hold_position, hold) in &wall.holds {
|
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 = move || problem.get().map(|o| o.and_then(|p| p.holds.get(&hold_position).copied()));
|
||||||
let role = Signal::derive(role);
|
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);
|
cells.push(cell);
|
||||||
}
|
}
|
||||||
let grid_classes = format!("grid grid-rows-{} grid-cols-{} gap-1", wall.rows, wall.cols,);
|
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
|
// TODO: refactor this to use the Problem component
|
||||||
#[component]
|
#[component]
|
||||||
#[tracing::instrument(skip_all)]
|
#[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");
|
tracing::trace!("Enter");
|
||||||
|
|
||||||
move || {
|
move || {
|
||||||
let role = role.get()?;
|
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 {
|
let role_classes = match role {
|
||||||
Some(HoldRole::Start) => Some("outline outline-offset-2 outline-green-500"),
|
Some(HoldRole::Start) => Some("outline outline-offset-2 outline-green-500"),
|
||||||
Some(HoldRole::Normal) => Some("outline outline-offset-2 outline-blue-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"),
|
Some(HoldRole::End) => Some("outline outline-offset-2 outline-red-500"),
|
||||||
None => Some("brightness-50"),
|
None => Some("brightness-50"),
|
||||||
};
|
};
|
||||||
let mut s = "bg-sky-100 aspect-square rounded hover:brightness-125".to_string();
|
|
||||||
if let Some(c) = role_classes {
|
if let Some(c) = role_classes {
|
||||||
s.push(' ');
|
class.push(' ');
|
||||||
s.push_str(c);
|
class.push_str(c);
|
||||||
}
|
}
|
||||||
s
|
}
|
||||||
};
|
|
||||||
|
|
||||||
let img = hold.image.as_ref().map(|img| {
|
let img = hold.image.as_ref().map(|img| {
|
||||||
let srcset = img.srcset();
|
let srcset = img.srcset();
|
||||||
|
Loading…
x
Reference in New Issue
Block a user