leptos_components/input/
search.rs1use components_core::{BASE_CLASS, concat};
2use leptos::prelude::*;
3use leptos::{IntoView, component, view};
4
5use crate::icons::{Filter as FilterIcon, Search as SearchIcon};
6use crate::tag::Tag;
7
8#[derive(Clone, Debug)]
9pub struct Filter {
10 pub label: String,
11 pub value: String,
12}
13
14#[component]
15pub fn InputSearch(
16 #[prop(into, optional)] filters: Option<Vec<Filter>>,
17 #[prop(into, optional)] active_filters: Vec<Filter>,
18 #[prop(into)] on_change_filter: Callback<(Vec<Filter>,), ()>,
19) -> impl IntoView {
20 let (filter_modal, set_filter_modal) = signal(false);
21 let has_filter = filters.as_ref().map_or(false, |f| !f.is_empty());
22
23 let handle_select_filter = {
24 let active_filters = active_filters.clone();
25
26 move |filter: Filter, is_selected: bool| {
27 if !is_selected {
28 let mut new_filters = active_filters.clone();
29 new_filters.push(filter);
30 on_change_filter.run((new_filters,));
31 } else {
32 let new_filters = active_filters
33 .iter()
34 .filter(|f| f.value != filter.value)
35 .cloned()
36 .collect();
37 on_change_filter.run((new_filters,));
38 }
39 }
40 };
41
42 let handle_close_on_click_input = move || {
43 if has_filter {
44 set_filter_modal.set(false);
45 }
46 };
47
48 view! {
49 <div class=concat!(BASE_CLASS, "-input-search-container")>
50 <label
51 class=crate::tw!(
52 concat!(BASE_CLASS, "-input-search"),
53 has_filter.then_some(concat!(BASE_CLASS, "-input-search--filter"))
54 )
55 >
56 <SearchIcon size=24 />
57 <input
58 type="text"
59 placeholder="Buscar"
60 on:click=move |_| handle_close_on_click_input()
61 class="text-caption"
62 />
63 </label>
64 <div class=concat!(BASE_CLASS, "-input-search__filter")>
65 {has_filter.then(|| view! {
66 <button on:click=move |_| set_filter_modal.update(|v| *v = !*v) tabindex="0">
67 <FilterIcon size=24 />
68 </button>
69 })}
70 <div
71 class=crate::tw!(
72 concat!(BASE_CLASS, "-input-search-backdrop__content"),
73 filter_modal.get().then_some(concat!(BASE_CLASS, "-input-search-backdrop__content--open"))
74 .unwrap_or(concat!(BASE_CLASS, "-input-search-backdrop__content--closed"))
75 )
76 >
77 {filter_modal.get().then(|| {
78 view! {
79 <ul class=concat!(BASE_CLASS, "-input-search-backdrop__list")>
80 {filters.map(|filters| filters.iter().map(|filter| {
81 let is_selected = active_filters.iter().any(|f| f.value == filter.value);
82 let filter = filter.clone();
83 let handle_select_filter = handle_select_filter.clone();
84 view! {
85 <li
86 on:click=move |_| handle_select_filter(filter.clone(), is_selected)
87 >
88 <Tag
89 selected=is_selected
90 label=filter.label.clone()
91 />
92 </li>
93 }
94 }).collect::<Vec<_>>()).unwrap_or_default()}
95 </ul>
96 }})}
97 </div>
98 </div>
99 </div>
100 }
101}