1use dioxus::prelude::*;
2
3use hyle::{FieldType, Primitive};
4
5use crate::context::use_hyle_components;
6use crate::types::{field_type_key, HyleFilterField, HyleFilterFieldProps, HyleFiltersState};
7
8#[component]
34pub fn FilterField(
35 state: HyleFiltersState,
36 field_key: String,
37 render: Option<fn(HyleFilterFieldProps) -> Element>,
38) -> Element {
39 let filter_field: Option<HyleFilterField> = state
40 .fields
41 .read()
42 .iter()
43 .find(|f| f.key == field_key)
44 .cloned();
45
46 let Some(ff) = filter_field else {
47 return rsx! {};
48 };
49
50 let key = ff.key.clone();
51 let value = state
52 .form_data
53 .read()
54 .get(&key)
55 .cloned()
56 .unwrap_or_default();
57
58 let set: Callback<String> = {
59 let key = key.clone();
60 Callback::new(move |v: String| state.set_field.call((key.clone(), v)))
61 };
62
63 if let Some(ref render_fn) = ff.render {
65 return render_fn(HyleFilterFieldProps {
66 key: ff.key,
67 label: ff.label,
68 field: ff.field,
69 options: ff.options,
70 value,
71 set,
72 });
73 }
74
75 if let Some(render_fn) = render {
77 return render_fn(HyleFilterFieldProps {
78 key: ff.key,
79 label: ff.label,
80 field: ff.field,
81 options: ff.options,
82 value,
83 set,
84 });
85 }
86
87 let props = HyleFilterFieldProps {
89 key: ff.key.clone(),
90 label: ff.label.clone(),
91 field: ff.field.clone(),
92 options: ff.options.clone(),
93 value: value.clone(),
94 set: set.clone(),
95 };
96 if let Some(components) = use_hyle_components() {
97 let type_key = field_type_key(&ff.field.field_type);
98 if let Some(render_fn) = components.filters.get(type_key).copied() {
99 return render_fn(props);
100 }
101 }
102
103 default_input(ff, value, set)
105}
106
107fn default_input(ff: HyleFilterField, value: String, set: Callback<String>) -> Element {
110 match &ff.field.field_type {
111 FieldType::Primitive {
112 primitive: Primitive::Boolean,
113 } => boolean_select(ff.key, ff.label, value, set),
114
115 FieldType::Reference { .. } => {
116 reference_select(ff.key, ff.label, value, ff.options.unwrap_or_default(), set)
117 }
118
119 _ => text_input(ff, value, set),
120 }
121}
122
123fn boolean_select(name: String, label: String, value: String, set: Callback<String>) -> Element {
124 rsx! {
125 select {
126 name: "{name}",
127 aria_label: "{label}",
128 onchange: move |e| set.call(e.value()),
129 option { value: "", selected: value.is_empty(), "Any" }
130 option { value: "true", selected: value == "true", "Yes" }
131 option { value: "false", selected: value == "false", "No" }
132 }
133 }
134}
135
136fn reference_select(
137 name: String,
138 label: String,
139 value: String,
140 options: Vec<(String, String)>,
141 set: Callback<String>,
142) -> Element {
143 rsx! {
144 select {
145 name: "{name}",
146 aria_label: "{label}",
147 onchange: move |e| set.call(e.value()),
148 option { value: "", selected: value.is_empty(), "All {label}s" }
149 for (id, display) in options {
150 option { key: "{id}", value: "{id}", selected: value == id, "{display}" }
151 }
152 }
153 }
154}
155
156fn text_input(ff: HyleFilterField, value: String, set: Callback<String>) -> Element {
157 let input_type = ff
158 .field
159 .options
160 .input
161 .as_ref()
162 .map(|i| i.kind.clone())
163 .unwrap_or_else(|| "text".to_owned());
164
165 rsx! {
166 input {
167 r#type: "{input_type}",
168 name: "{ff.key}",
169 aria_label: "{ff.label}",
170 placeholder: "{ff.label}",
171 value: "{value}",
172 oninput: move |e| set.call(e.value()),
173 }
174 }
175}