232 lines
8.0 KiB
Rust
232 lines
8.0 KiB
Rust
use leptos::{either::Either, ev::MouseEvent, prelude::*};
|
|
use reactive_stores::Field;
|
|
use src_common::models::loot_filter::{
|
|
Filter, FilterGroup, FilterGroupStoreFields, FilterLeaf, FilterLeafStoreFields, FilterRemain,
|
|
FilterRemainStoreFields, FilterStoreFields,
|
|
};
|
|
|
|
use crate::components::icons;
|
|
|
|
fn group(
|
|
filter: Field<Filter>,
|
|
group: Field<FilterGroup>,
|
|
on_action: Callback<(Action,)>,
|
|
selected: Field<Option<Field<Filter>>>,
|
|
root: bool,
|
|
) -> impl IntoView {
|
|
let header = move || {
|
|
if root {
|
|
"Root".to_string()
|
|
} else {
|
|
filter.id().get().to_string()
|
|
}
|
|
};
|
|
let (expanded, set_expanded) = signal(false);
|
|
|
|
let new_rule = move |ev: MouseEvent| {
|
|
ev.stop_propagation();
|
|
group.filters().write().push(Filter::default_leaf());
|
|
set_expanded.set(true);
|
|
};
|
|
let new_group = move |ev: MouseEvent| {
|
|
ev.stop_propagation();
|
|
group.filters().write().push(Filter::default_group());
|
|
set_expanded.set(true);
|
|
};
|
|
let nav_icon = move || {
|
|
view! {
|
|
<button on:click=move |ev| {
|
|
ev.stop_propagation();
|
|
set_expanded
|
|
.update(|v| {
|
|
*v = !*v;
|
|
});
|
|
}>
|
|
{if expanded.get() {
|
|
Either::Left(view! { <icons::ChevronDown16Solid /> })
|
|
} else {
|
|
Either::Right(view! { <icons::ChevronRight16Solid /> })
|
|
}}
|
|
</button>
|
|
}
|
|
};
|
|
let active = move || match selected.get() {
|
|
Some(f) => filter.id().get() == f.id().get(),
|
|
None => false,
|
|
};
|
|
view! {
|
|
<li>
|
|
<a
|
|
class=("active", active)
|
|
on:click=move |_| {
|
|
on_action.run((Action::Select(filter),));
|
|
}
|
|
>
|
|
<div class="flex gap-2">
|
|
{nav_icon}
|
|
<button on:click=move |ev| {
|
|
ev.stop_propagation();
|
|
on_action.run((Action::Up,));
|
|
}>
|
|
<icons::ArrowUp16Solid />
|
|
</button>
|
|
<button on:click=move |ev| {
|
|
ev.stop_propagation();
|
|
on_action.run((Action::Down,));
|
|
}>
|
|
<icons::ArrowDown16Solid />
|
|
</button>
|
|
</div>
|
|
{header}
|
|
<div class="flex gap-2">
|
|
<button class="tooltip" data-tip="New Rule">
|
|
<icons::Plus16Solid on:click=new_rule />
|
|
</button>
|
|
<button class="tooltip" data-tip="New Group">
|
|
<icons::FolderPlus16Solid on:click=new_group />
|
|
</button>
|
|
<input
|
|
type="checkbox"
|
|
class="toggle toggle-xs"
|
|
disabled=root
|
|
prop:checked=move || filter.enabled().get()
|
|
on:click=move |ev| {
|
|
ev.stop_propagation();
|
|
}
|
|
on:change:target=move |ev| {
|
|
filter.enabled().set(event_target_checked(&ev));
|
|
}
|
|
/>
|
|
</div>
|
|
</a>
|
|
<ul class=("hidden", move || !expanded.get())>
|
|
<For
|
|
each=move || group.filters()
|
|
key=|row| row.read().id.to_string()
|
|
children=move |filter| {
|
|
let id = filter.clone().id().get();
|
|
view! {
|
|
<Filter
|
|
filter
|
|
selected
|
|
on_action=move |action| {
|
|
let i = group
|
|
.filters()
|
|
.into_iter()
|
|
.position(|f| f.id().get() == id)
|
|
.unwrap();
|
|
match action {
|
|
Action::Up => {
|
|
if i > 0 {
|
|
group.filters().write().swap(i, i - 1);
|
|
}
|
|
}
|
|
Action::Down => {
|
|
if i < group.filters().get().len() - 1 {
|
|
group.filters().write().swap(i, i + 1);
|
|
}
|
|
}
|
|
a @ Action::Select(_) => {
|
|
on_action.run((a,));
|
|
}
|
|
}
|
|
}
|
|
/>
|
|
}
|
|
}
|
|
/>
|
|
</ul>
|
|
</li>
|
|
}
|
|
}
|
|
|
|
fn leaf(
|
|
filter: Field<Filter>,
|
|
leaf: Field<FilterLeaf>,
|
|
on_action: Callback<(Action,)>,
|
|
selected: Field<Option<Field<Filter>>>,
|
|
) -> impl IntoView {
|
|
let header = move || filter.id().get().to_string();
|
|
|
|
let icon = move || {
|
|
if leaf.show().get() {
|
|
Either::Left(view! { <icons::Eye16Solid /> })
|
|
} else {
|
|
Either::Right(view! { <icons::EyeSlash16Solid /> })
|
|
}
|
|
};
|
|
let active = move || match selected.get() {
|
|
Some(f) => filter.id().get() == f.id().get(),
|
|
None => false,
|
|
};
|
|
view! {
|
|
<li>
|
|
<a
|
|
class=("active", active)
|
|
on:click=move |_| {
|
|
on_action.run((Action::Select(filter),));
|
|
}
|
|
>
|
|
<div class="flex gap-2">
|
|
{icon}
|
|
<button on:click=move |ev| {
|
|
ev.stop_propagation();
|
|
on_action.run((Action::Up,));
|
|
}>
|
|
<icons::ArrowUp16Solid />
|
|
</button>
|
|
<button on:click=move |ev| {
|
|
ev.stop_propagation();
|
|
on_action.run((Action::Down,));
|
|
}>
|
|
<icons::ArrowDown16Solid />
|
|
</button>
|
|
</div>
|
|
{header}
|
|
<div class="flex gap-2">
|
|
<input
|
|
type="checkbox"
|
|
class="toggle toggle-xs"
|
|
prop:checked=move || filter.enabled().get()
|
|
on:click=move |ev| {
|
|
ev.stop_propagation();
|
|
}
|
|
on:change:target=move |ev| {
|
|
filter.enabled().set(event_target_checked(&ev));
|
|
}
|
|
/>
|
|
</div>
|
|
</a>
|
|
</li>
|
|
}
|
|
}
|
|
|
|
pub enum Action {
|
|
Up,
|
|
Down,
|
|
Select(Field<Filter>),
|
|
}
|
|
|
|
#[component]
|
|
pub fn Filter(
|
|
#[prop(into)] filter: Field<Filter>,
|
|
#[prop(into)] on_action: Callback<(Action,)>,
|
|
#[prop(into)] selected: Field<Option<Field<Filter>>>,
|
|
#[prop(optional)] root: bool,
|
|
) -> impl IntoView {
|
|
let remain = filter.remain();
|
|
match remain.get_untracked() {
|
|
FilterRemain::Leaf(_) => {
|
|
leaf(filter, remain.leaf_0().unwrap().into(), on_action, selected).into_any()
|
|
}
|
|
FilterRemain::Group(_) => group(
|
|
filter,
|
|
remain.group_0().unwrap().into(),
|
|
on_action,
|
|
selected,
|
|
root,
|
|
)
|
|
.into_any(),
|
|
}
|
|
}
|