From 0baa57d48da28fd4257131223983199cd6766260 Mon Sep 17 00:00:00 2001 From: NotAShelf Date: Wed, 11 Mar 2026 17:02:29 +0300 Subject: [PATCH] pinakes-ui: add `SettingsSection` widget target; align location strings with schema constants Signed-off-by: NotAShelf Change-Id: I9a5b91457136254fdf5fa582899079e46a6a6964 --- crates/pinakes-ui/src/plugin_ui/widget.rs | 79 +++++++++++++++++++---- 1 file changed, 65 insertions(+), 14 deletions(-) diff --git a/crates/pinakes-ui/src/plugin_ui/widget.rs b/crates/pinakes-ui/src/plugin_ui/widget.rs index 35ba4fa..362367c 100644 --- a/crates/pinakes-ui/src/plugin_ui/widget.rs +++ b/crates/pinakes-ui/src/plugin_ui/widget.rs @@ -4,10 +4,15 @@ //! predefined locations. Unlike full pages, widgets have no data sources of //! their own and render with empty data context. -use dioxus::prelude::*; -use pinakes_plugin_api::UiWidget; +use std::collections::HashMap; -use super::{data::PluginPageData, renderer::render_element}; +use dioxus::prelude::*; +use pinakes_plugin_api::{ActionDefinition, UiWidget, widget_location}; + +use super::{ + data::PluginPageData, + renderer::{RenderContext, render_element}, +}; use crate::client::ApiClient; /// Predefined injection points in the host UI. @@ -21,6 +26,7 @@ pub enum WidgetLocation { LibrarySidebar, DetailPanel, SearchFilters, + SettingsSection, } impl WidgetLocation { @@ -28,10 +34,11 @@ impl WidgetLocation { #[must_use] pub const fn as_str(self) -> &'static str { match self { - Self::LibraryHeader => "library_header", - Self::LibrarySidebar => "library_sidebar", - Self::DetailPanel => "detail_panel", - Self::SearchFilters => "search_filters", + Self::LibraryHeader => widget_location::LIBRARY_HEADER, + Self::LibrarySidebar => widget_location::LIBRARY_SIDEBAR, + Self::DetailPanel => widget_location::DETAIL_PANEL, + Self::SearchFilters => widget_location::SEARCH_FILTERS, + Self::SettingsSection => widget_location::SETTINGS_SECTION, } } } @@ -41,11 +48,13 @@ impl WidgetLocation { pub struct WidgetContainerProps { /// Injection point to render widgets for. pub location: WidgetLocation, - /// All widgets from all plugins (plugin_id, widget) pairs. - pub widgets: Vec<(String, UiWidget)>, - /// API client (unused by widgets themselves but threaded through for - /// consistency with the rest of the plugin UI system). - pub client: Signal, + + /// All widgets from all plugins (`plugin_id`, widget) pairs. + pub widgets: Vec<(String, UiWidget)>, + + /// API client. It is actually unused by widgets themselves but threaded + /// through for consistency with the rest of the plugin UI system. + pub client: Signal, } /// Renders all widgets registered for a specific [`WidgetLocation`]. @@ -107,12 +116,54 @@ pub struct WidgetViewRendererProps { #[component] pub fn WidgetViewRenderer(props: WidgetViewRendererProps) -> Element { let empty_data = PluginPageData::default(); + let feedback = use_signal(|| None::<(String, bool)>); + let navigate = use_signal(|| None::); + let refresh = use_signal(|| 0u32); + let modal = use_signal(|| None::); + let local_state = use_signal(HashMap::::new); + let ctx = RenderContext { + client: props.client, + feedback, + navigate, + refresh, + modal, + local_state, + }; + let empty_actions: HashMap = HashMap::new(); rsx! { div { class: "plugin-widget", "data-plugin-id": props.plugin_id.clone(), - "data-widget-id": props.widget.id.clone(), - { render_element(&props.widget.content, &empty_data, props.client) } + "data-widget-id": props.widget.id, + { render_element(&props.widget.content, &empty_data, &empty_actions, ctx) } } } } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_widget_location_settings_section_str() { + assert_eq!(WidgetLocation::SettingsSection.as_str(), "settings_section"); + } + + #[test] + fn test_widget_location_all_variants_unique() { + let locations = [ + WidgetLocation::LibraryHeader, + WidgetLocation::LibrarySidebar, + WidgetLocation::DetailPanel, + WidgetLocation::SearchFilters, + WidgetLocation::SettingsSection, + ]; + let strings: Vec<&str> = locations.iter().map(|l| l.as_str()).collect(); + let unique: std::collections::HashSet<_> = strings.iter().collect(); + assert_eq!( + strings.len(), + unique.len(), + "all location strings must be unique" + ); + } +}