[pdm-devel] [PATCH datacenter-manager 4/7] ui: add possibility to insert into search box
Dominik Csapak
d.csapak at proxmox.com
Wed Apr 16 13:49:22 CEST 2025
by implementing a 'SearchProvider' context. This enables us to
insert a search term from everywhere. This can be helpful e.g. if we
want to prefill the search box with a specific pattern
Signed-off-by: Dominik Csapak <d.csapak at proxmox.com>
---
ui/Cargo.toml | 1 +
ui/src/lib.rs | 3 +++
ui/src/main.rs | 17 ++++++++++++-----
ui/src/search_provider.rs | 35 +++++++++++++++++++++++++++++++++++
ui/src/widget/search_box.rs | 26 +++++++++++++++++++++-----
5 files changed, 72 insertions(+), 10 deletions(-)
create mode 100644 ui/src/search_provider.rs
diff --git a/ui/Cargo.toml b/ui/Cargo.toml
index 10345a2..96fd07e 100644
--- a/ui/Cargo.toml
+++ b/ui/Cargo.toml
@@ -42,6 +42,7 @@ pbs-api-types = "0.2.0"
pdm-api-types = { version = "0.1", path = "../lib/pdm-api-types" }
pdm-ui-shared = { version = "0.1", path = "../lib/pdm-ui-shared" }
pdm-client = { version = "0.1", path = "../lib/pdm-client" }
+pdm-search = { version = "0.1", path = "../lib/pdm-search" }
[patch.crates-io]
# proxmox-client = { path = "../../proxmox/proxmox-client" }
diff --git a/ui/src/lib.rs b/ui/src/lib.rs
index e3755ec..edb50f9 100644
--- a/ui/src/lib.rs
+++ b/ui/src/lib.rs
@@ -21,6 +21,9 @@ pub use remotes::RemoteConfigPanel;
mod top_nav_bar;
pub use top_nav_bar::TopNavBar;
+mod search_provider;
+pub use search_provider::SearchProvider;
+
mod dashboard;
pub use dashboard::Dashboard;
use yew_router::prelude::RouterScopeExt;
diff --git a/ui/src/main.rs b/ui/src/main.rs
index 6e2c9b2..be0c10c 100644
--- a/ui/src/main.rs
+++ b/ui/src/main.rs
@@ -22,7 +22,7 @@ use proxmox_yew_comp::{
//use pbs::MainMenu;
use pdm_api_types::subscription::{RemoteSubscriptionState, RemoteSubscriptions};
-use pdm_ui::{register_pve_tasks, MainMenu, RemoteList, TopNavBar};
+use pdm_ui::{register_pve_tasks, MainMenu, RemoteList, SearchProvider, TopNavBar};
type MsgRemoteList = Result<RemoteList, Error>;
@@ -46,6 +46,7 @@ struct DatacenterManagerApp {
remote_list: RemoteList,
remote_list_error: Option<String>,
remote_list_timeout: Option<Timeout>,
+ search_provider: SearchProvider,
}
async fn check_subscription() -> Msg {
@@ -166,6 +167,7 @@ impl Component for DatacenterManagerApp {
remote_list: Vec::new().into(),
remote_list_error: None,
remote_list_timeout: None,
+ search_provider: SearchProvider::new(),
};
this.on_login(ctx, false);
@@ -258,10 +260,15 @@ impl Component for DatacenterManagerApp {
.with_optional_child(subscription_alert);
let context = self.remote_list.clone();
-
- DesktopApp::new(
- html! {<ContextProvider<RemoteList> {context}>{body}</ContextProvider<RemoteList>>},
- )
+ let search_context = self.search_provider.clone();
+
+ DesktopApp::new(html! {
+ <ContextProvider<SearchProvider> context={search_context}>
+ <ContextProvider<RemoteList> {context}>
+ {body}
+ </ContextProvider<RemoteList>>
+ </ContextProvider<SearchProvider>>
+ })
.into()
}
}
diff --git a/ui/src/search_provider.rs b/ui/src/search_provider.rs
new file mode 100644
index 0000000..441cc2b
--- /dev/null
+++ b/ui/src/search_provider.rs
@@ -0,0 +1,35 @@
+use yew::Callback;
+
+use pwt::state::{SharedState, SharedStateObserver};
+
+use pdm_search::Search;
+
+#[derive(Clone, PartialEq)]
+pub struct SearchProvider {
+ state: SharedState<String>,
+}
+
+impl SearchProvider {
+ pub fn new() -> Self {
+ Self {
+ state: SharedState::new("".into()),
+ }
+ }
+
+ pub fn add_listener(
+ &self,
+ cb: impl Into<Callback<SharedState<String>>>,
+ ) -> SharedStateObserver<String> {
+ self.state.add_listener(cb)
+ }
+
+ pub fn search(&self, search_term: Search) {
+ **self.state.write() = search_term.to_string();
+ }
+}
+
+pub fn get_search_provider<T: yew::Component>(ctx: &yew::Context<T>) -> Option<SearchProvider> {
+ let (provider, _context_listener) = ctx.link().context(Callback::from(|_| {}))?;
+
+ Some(provider)
+}
diff --git a/ui/src/widget/search_box.rs b/ui/src/widget/search_box.rs
index 0aeedb7..6b2478f 100644
--- a/ui/src/widget/search_box.rs
+++ b/ui/src/widget/search_box.rs
@@ -9,13 +9,15 @@ use yew::{
};
use pwt::{
- dom::focus::FocusTracker,
- dom::IntoHtmlElement,
+ dom::{focus::FocusTracker, IntoHtmlElement},
prelude::*,
props::CssLength,
+ state::{SharedState, SharedStateObserver},
widget::{form::Field, Container},
};
+use crate::search_provider::get_search_provider;
+
use super::ResourceTree;
#[derive(Properties, PartialEq)]
@@ -35,7 +37,7 @@ impl From<SearchBox> for VNode {
}
pub enum Msg {
- ChangeTerm(String),
+ ChangeTerm(String, bool), // force value
FocusChange(bool),
ToggleFocus,
}
@@ -48,6 +50,8 @@ pub struct PdmSearchBox {
focus: bool,
global_shortcut_listener: Closure<dyn Fn(KeyboardEvent)>,
toggle_focus: bool,
+ _observer: Option<SharedStateObserver<String>>,
+ force_value: bool,
}
impl Component for PdmSearchBox {
@@ -57,6 +61,14 @@ impl Component for PdmSearchBox {
fn create(ctx: &yew::Context<Self>) -> Self {
let link = ctx.link().clone();
+ let _observer = get_search_provider(ctx).map(|search| {
+ search.add_listener(ctx.link().batch_callback(|value: SharedState<String>| {
+ vec![
+ Msg::ToggleFocus,
+ Msg::ChangeTerm(value.read().clone(), true),
+ ]
+ }))
+ });
Self {
search_field_ref: Default::default(),
search_box_ref: Default::default(),
@@ -72,13 +84,16 @@ impl Component for PdmSearchBox {
_ => {}
}
})),
+ _observer,
+ force_value: false,
}
}
fn update(&mut self, _ctx: &Context<Self>, msg: Self::Message) -> bool {
match msg {
- Msg::ChangeTerm(term) => {
+ Msg::ChangeTerm(term, force_value) => {
self.search_term = term;
+ self.force_value = force_value;
true
}
Msg::FocusChange(focus) => {
@@ -122,7 +137,8 @@ impl Component for PdmSearchBox {
Field::new()
.placeholder(tr!("Search (Ctrl+Space / Ctrl+Shift+F)"))
.node_ref(self.search_field_ref.clone())
- .on_input(ctx.link().callback(Msg::ChangeTerm)),
+ .value(self.force_value.then_some(self.search_term.clone()))
+ .on_input(ctx.link().callback(|term| Msg::ChangeTerm(term, false))),
)
.with_child(search_result)
.into()
--
2.39.5
More information about the pdm-devel
mailing list