use crate::Decorated;
use crate::{IntoText, TextSource};
use day_core::{BuildCx, Flex, Piece, RNode, with_tree};
use day_reactive::{Binding, bind_seeded};
use day_spec::Event;
use std::cell::RefCell;
use std::rc::Rc;
pub use day_spec::controls::searchfield::*;
pub struct SearchField<S: Binding<String>> {
query: S,
placeholder: Option<TextSource>,
}
pub fn search_field<S: Binding<String>>(query: S) -> SearchField<S> {
SearchField {
query,
placeholder: None,
}
}
#[crate::builder(SearchFieldBuilder)]
impl<S: Binding<String>> SearchField<S> {
pub fn placeholder<M>(mut self, t: impl IntoText<M>) -> Self {
self.placeholder = Some(t.into_text());
self
}
}
impl<S: Binding<String>> Piece for SearchField<S> {
fn build(self, cx: &mut BuildCx) -> RNode {
let SearchField { query, placeholder } = self;
let initial = query.peek();
let ph = placeholder.map(|p| p.initial()).unwrap_or_default();
let node = cx.leaf(
KIND,
&SearchProps {
text: initial.clone(),
placeholder: ph,
},
Flex {
grow_w: true,
..Default::default()
},
);
let guard: Rc<RefCell<Option<String>>> = Rc::new(RefCell::new(None));
let g = guard.clone();
let q = query.clone();
bind_seeded(
initial,
move || q.read(),
move |t: &String| {
let from_native = g.borrow_mut().take().as_deref() == Some(t.as_str());
if !from_native {
with_tree(|tr| {
tr.patch(node, Box::new(SearchPatch::SetText(t.clone())), false)
});
}
},
);
cx.on(node, move |ev| {
if let Event::TextChanged(t) = ev {
*guard.borrow_mut() = Some(t.clone());
query.write(t.clone());
}
});
node
}
}
#[cfg(feature = "conformance")]
pub mod conformance {
use crate::*;
use day_core::conformance::{Case, Drive};
use day_reactive::Signal;
use super::{KIND, search_field};
#[day_macros::test(day_core)]
fn searchfield_query_two_way() -> Case {
Case::new()
.proves(KIND)
.page(|| {
let query = Signal::new(String::new());
column((
label(move || format!("query {}", query.get()))
.id(day_core::id!(searchfield::query)),
search_field(query)
.placeholder("Search")
.id(day_core::id!(searchfield::search)),
button("Clear")
.action(move || query.set(String::new()))
.id(day_core::id!(searchfield::clear)),
))
})
.drive(|d: Drive| async move {
d.input("searchfield::search", "choc").await?;
d.assert_text("searchfield::query", "query choc").await?;
d.tap("searchfield::clear").await?;
d.assert_text("searchfield::query", "query ").await?;
d.assert_text("searchfield::search", "").await
})
}
}
pub fn support() -> day_spec::Support {
day_core::capability(day_spec::Cap::SearchField)
}