From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from firstgate.proxmox.com (firstgate.proxmox.com [212.224.123.68]) by lore.proxmox.com (Postfix) with ESMTPS id 4002C1FF190 for ; Fri, 10 Jan 2025 12:29:35 +0100 (CET) Received: from firstgate.proxmox.com (localhost [127.0.0.1]) by firstgate.proxmox.com (Proxmox) with ESMTP id AC8BB2AC93; Fri, 10 Jan 2025 12:29:21 +0100 (CET) From: Dietmar Maurer To: pdm-devel@lists.proxmox.com Date: Fri, 10 Jan 2025 12:28:44 +0100 Message-Id: <20250110112844.184291-1-dietmar@proxmox.com> X-Mailer: git-send-email 2.39.5 MIME-Version: 1.0 X-SPAM-LEVEL: Spam detection results: 0 AWL -0.561 Adjusted score from AWL reputation of From: address BAYES_00 -1.9 Bayes spam probability is 0 to 1% DMARC_MISSING 0.1 Missing DMARC policy KAM_DMARC_STATUS 0.01 Test Rule for DKIM or SPF Failure with Strict Alignment KAM_LAZY_DOMAIN_SECURITY 1 Sending domain does not have any anti-forgery methods RCVD_IN_VALIDITY_CERTIFIED_BLOCKED 0.001 ADMINISTRATOR NOTICE: The query to Validity was blocked. See https://knowledge.validity.com/hc/en-us/articles/20961730681243 for more information. RCVD_IN_VALIDITY_RPBL_BLOCKED 0.001 ADMINISTRATOR NOTICE: The query to Validity was blocked. See https://knowledge.validity.com/hc/en-us/articles/20961730681243 for more information. RCVD_IN_VALIDITY_SAFE_BLOCKED 0.001 ADMINISTRATOR NOTICE: The query to Validity was blocked. See https://knowledge.validity.com/hc/en-us/articles/20961730681243 for more information. RDNS_NONE 0.793 Delivered to internal network by a host with no rDNS SPF_HELO_NONE 0.001 SPF: HELO does not publish an SPF Record SPF_NONE 0.001 SPF: sender does not publish an SPF Record Subject: [pdm-devel] [RFC: pdm] ui: make layout more consistent using new ContentSpacer widget X-BeenThere: pdm-devel@lists.proxmox.com X-Mailman-Version: 2.1.29 Precedence: list List-Id: Proxmox Datacenter Manager development discussion List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Reply-To: Proxmox Datacenter Manager development discussion Content-Type: text/plain; charset="us-ascii" Content-Transfer-Encoding: 7bit Errors-To: pdm-devel-bounces@lists.proxmox.com Sender: "pdm-devel" Signed-off-by: Dietmar Maurer --- This only change the "Desktop" Theme layout. TODO: change borders to shadows... ui/css/crisp-yew-style.scss | 12 ++++ ui/css/desktop-yew-style.scss | 5 ++ ui/css/pdm.scss | 26 ++++++- ui/src/administration/mod.rs | 28 ++++++-- ui/src/certificates.rs | 30 ++++++-- ui/src/configuration/mod.rs | 25 ++++--- ui/src/configuration/other/mod.rs | 9 ++- ui/src/configuration/other/webauthn.rs | 1 - ui/src/main_menu.rs | 22 +++--- ui/src/widget/content_spacer.rs | 97 ++++++++++++++++++++++++++ ui/src/widget/mod.rs | 3 + 11 files changed, 223 insertions(+), 35 deletions(-) create mode 100644 ui/src/widget/content_spacer.rs diff --git a/ui/css/crisp-yew-style.scss b/ui/css/crisp-yew-style.scss index d254b53..df8bb9c 100644 --- a/ui/css/crisp-yew-style.scss +++ b/ui/css/crisp-yew-style.scss @@ -1,2 +1,14 @@ @import "../pwt-assets/scss/crisp-yew-style"; @import "pdm"; + +.proxmox-content-spacer { + @include color-scheme-vars("neutral"); + + &.proxmox-content-spacer-with-one-child { + padding: 0; + } + + &.proxmox-content-spacer-with-one-child > * { + border: 0; + } +} diff --git a/ui/css/desktop-yew-style.scss b/ui/css/desktop-yew-style.scss index 3d1ac0a..a14a277 100644 --- a/ui/css/desktop-yew-style.scss +++ b/ui/css/desktop-yew-style.scss @@ -1,2 +1,7 @@ @import "../pwt-assets/scss/desktop-yew-style"; @import "pdm"; + +.proxmox-content-spacer { + padding: var(--pwt-spacer-4); + gap: var(--pwt-spacer-4); +} diff --git a/ui/css/pdm.scss b/ui/css/pdm.scss index 4ef22e2..6597f31 100644 --- a/ui/css/pdm.scss +++ b/ui/css/pdm.scss @@ -60,7 +60,27 @@ } :root.pwt-dark-mode { - .fa-memory, .fa-cpu { - filter: invert(90%); - } + .fa-memory, + .fa-cpu { + filter: invert(90%); + } +} + +.proxmox-content-spacer { + @include color-scheme-vars("surface"); + color: var(--pwt-color); + background-color: var(--pwt-color-background); + + padding: var(--pwt-spacer-2); + gap: var(--pwt-spacer-2); + + display: flex; + flex-direction: column; + + & > * { + @include color-scheme-vars("neutral"); + border: 1px solid var(--pwt-color-border); + color: var(--pwt-color); + background-color: var(--pwt-color-background); + } } diff --git a/ui/src/administration/mod.rs b/ui/src/administration/mod.rs index 15d04ae..eeb9efa 100644 --- a/ui/src/administration/mod.rs +++ b/ui/src/administration/mod.rs @@ -18,6 +18,8 @@ use pwt_macros::builder; use proxmox_yew_comp::{AptPackageManager, AptRepositories, ExistingProduct, Syslog, Tasks}; +use crate::widget::ContentSpacer; + #[derive(Clone, PartialEq, Properties)] #[builder] pub struct ServerAdministration { @@ -73,8 +75,9 @@ impl Component for PdmServerAdministration { .label("Updates") .icon_class("fa fa-refresh"), move |_| { - AptPackageManager::new() - .enable_upgrade(enable_upgrade) + ContentSpacer::new() + .class("pwt-flex-fill") + .with_child(AptPackageManager::new().enable_upgrade(enable_upgrade)) .into() }, ) @@ -83,21 +86,36 @@ impl Component for PdmServerAdministration { .key("repositories") .label("Repositories") .icon_class("fa fa-files-o"), - |_| AptRepositories::new().product(ExistingProduct::PDM).into(), + |_| { + ContentSpacer::new() + .class("pwt-flex-fit") + .with_child(AptRepositories::new().product(ExistingProduct::PDM)) + .into() + }, ) .with_item_builder( TabBarItem::new() .key("syslog") .label("Syslog") .icon_class("fa fa-list"), - |_| Syslog::new().into(), // fixme: use JournalView instead? + |_| { + ContentSpacer::new() + .class("pwt-flex-fit") + .with_child(Syslog::new()) + .into() // fixme: use JournalView instead? + }, ) .with_item_builder( TabBarItem::new() .key("tasks") .label("Tasks") .icon_class("fa fa-list-alt"), - |_| Tasks::new().into(), + |_| { + ContentSpacer::new() + .class("pwt-flex-fit") + .with_child(Tasks::new()) + .into() + }, ); NavigationContainer::new().with_child(panel).into() diff --git a/ui/src/certificates.rs b/ui/src/certificates.rs index 39929b4..d348785 100644 --- a/ui/src/certificates.rs +++ b/ui/src/certificates.rs @@ -7,6 +7,8 @@ use proxmox_yew_comp::acme::{ AcmeAccountsPanel, AcmeDomainsPanel, AcmePluginsPanel, CertificateList, }; +use crate::widget::ContentSpacer; + #[function_component(CertificatesPanel)] pub fn certificates_panel() -> Html { let panel = TabPanel::new() @@ -19,23 +21,43 @@ pub fn certificates_panel() -> Html { TabBarItem::new() .key("certificate_List") .label("Certificates"), - |_| CertificateList::new().into(), + |_| { + ContentSpacer::new() + .class("pwt-flex-fit") + .with_child(CertificateList::new()) + .into() + }, ) .with_item_builder( TabBarItem::new().key("acme_domains").label("ACME Domains"), - |_| AcmeDomainsPanel::new().url("/config/certificate").into(), + |_| { + ContentSpacer::new() + .class("pwt-flex-fit") + .with_child(AcmeDomainsPanel::new().url("/config/certificate")) + .into() + }, ) .with_item_builder( TabBarItem::new() .key("acme_accounts") .label("ACME Accounts"), - |_| AcmeAccountsPanel::new().into(), + |_| { + ContentSpacer::new() + .class("pwt-flex-fit") + .with_child(AcmeAccountsPanel::new()) + .into() + }, ) .with_item_builder( TabBarItem::new() .key("acme_plugins") .label("Challenge Plugins"), - |_| AcmePluginsPanel::new().into(), + |_| { + ContentSpacer::new() + .class("pwt-flex-fit") + .with_child(AcmePluginsPanel::new()) + .into() + }, ); NavigationContainer::new().with_child(panel).into() diff --git a/ui/src/configuration/mod.rs b/ui/src/configuration/mod.rs index 15421a4..63e741e 100644 --- a/ui/src/configuration/mod.rs +++ b/ui/src/configuration/mod.rs @@ -1,7 +1,7 @@ use pwt::prelude::*; use pwt::props::StorageLocation; use pwt::state::NavigationContainer; -use pwt::widget::{Column, MiniScrollMode, Panel, TabBarItem, TabPanel}; +use pwt::widget::{Column, Container, MiniScrollMode, Panel, TabBarItem, TabPanel}; use proxmox_yew_comp::configuration::TimePanel; use proxmox_yew_comp::configuration::{DnsPanel, NetworkView}; @@ -11,6 +11,8 @@ use proxmox_yew_comp::UserPanel; mod other; pub use other::OtherPanel; +use crate::widget::ContentSpacer; + #[function_component(SystemConfiguration)] pub fn system_configuration() -> Html { let panel = TabPanel::new() @@ -50,14 +52,24 @@ pub fn access_control() -> Html { .key("user-management") .icon_class("fa fa-user") .label(tr!("User Management")), - |_| UserPanel::new().into(), + |_| { + ContentSpacer::new() + .class("pwt-flex-fit") + .with_child(UserPanel::new()) + .into() + }, ) .with_item_builder( TabBarItem::new() .key("two-factor") .icon_class("fa fa-key") .label(tr!("Two Factor Authentication")), - |_| TfaView::new().into(), + |_| { + ContentSpacer::new() + .class("pwt-flex-fit") + .with_child(TfaView::new()) + .into() + }, ); NavigationContainer::new().with_child(panel).into() @@ -65,19 +77,15 @@ pub fn access_control() -> Html { #[function_component(NetworkTimePanel)] pub fn create_network_time_panel() -> Html { - Column::new() + ContentSpacer::new() .class("pwt-flex-fit") - .padding(2) - .gap(4) .with_child( Panel::new() - .border(true) .title(tr!("Time")) .with_child(html! { }), ) .with_child( Panel::new() - .border(true) .title(tr!("DNS")) .with_child(html! { }), ) @@ -85,7 +93,6 @@ pub fn create_network_time_panel() -> Html { Panel::new() .min_height(200) .class("pwt-flex-fit") - .border(true) .title(tr!("Network Interfaces")) .with_child(NetworkView::new()), ) diff --git a/ui/src/configuration/other/mod.rs b/ui/src/configuration/other/mod.rs index f2435ac..cdd8e98 100644 --- a/ui/src/configuration/other/mod.rs +++ b/ui/src/configuration/other/mod.rs @@ -1,14 +1,13 @@ use pwt::prelude::*; -use pwt::widget::Column; + +use crate::widget::ContentSpacer; mod webauthn; #[function_component(OtherPanel)] pub fn create_other_panel() -> Html { - Column::new() - .class("pwt-flex-fill") - .padding(2) - .gap(4) + ContentSpacer::new() + .class("pwt-flex-fit") .with_child(html! { }) .into() } diff --git a/ui/src/configuration/other/webauthn.rs b/ui/src/configuration/other/webauthn.rs index 08bc09a..9748e3c 100644 --- a/ui/src/configuration/other/webauthn.rs +++ b/ui/src/configuration/other/webauthn.rs @@ -19,7 +19,6 @@ use proxmox_yew_comp::{ObjectGrid, ObjectGridRow}; #[function_component(WebauthnPanel)] pub fn webauthn_panel() -> Html { Panel::new() - .border(true) .title(tr!("WebAuthn TFA")) .with_child(object_grid()) .into() diff --git a/ui/src/main_menu.rs b/ui/src/main_menu.rs index e75eb90..3d2b5a0 100644 --- a/ui/src/main_menu.rs +++ b/ui/src/main_menu.rs @@ -16,6 +16,7 @@ use proxmox_yew_comp::{NotesView, XTermJs}; use pdm_api_types::remotes::RemoteType; +use crate::widget::ContentSpacer; use crate::{ AccessControl, CertificatesPanel, Dashboard, RemoteConfigPanel, RemoteList, ServerAdministration, SystemConfiguration, @@ -177,14 +178,14 @@ impl Component for PdmMainMenu { "notes", Some("fa fa-sticky-note-o"), move |_| { - NotesView::new("/config/notes") - .on_submit(|notes| async move { - proxmox_yew_comp::http_put( - "/config/notes", - Some(serde_json::to_value(¬es)?), - ) + let notes = NotesView::new("/config/notes").on_submit(|notes| async move { + proxmox_yew_comp::http_put("/config/notes", Some(serde_json::to_value(¬es)?)) .await - }) + }); + + ContentSpacer::new() + .class("pwt-flex-fit") + .with_child(notes) .into() }, ); @@ -274,7 +275,12 @@ impl Component for PdmMainMenu { } else { "fa fa-server" }), - |_| RemoteConfigPanel::new().into(), + |_| { + ContentSpacer::new() + .class("pwt-flex-fit") + .with_child(RemoteConfigPanel::new()) + .into() + }, remote_submenu, ); diff --git a/ui/src/widget/content_spacer.rs b/ui/src/widget/content_spacer.rs new file mode 100644 index 0000000..37f7e93 --- /dev/null +++ b/ui/src/widget/content_spacer.rs @@ -0,0 +1,97 @@ +use std::rc::Rc; + +use pwt::props::{AsClassesMut, AsCssStylesMut, CssStyles}; +use pwt::widget::Container; +use yew::prelude::*; +use yew::virtual_dom::{Key, VComp, VNode}; + +use pwt::prelude::*; + +#[derive(Clone, PartialEq, Properties)] +pub struct ContentSpacer { + /// The yew component key. + #[prop_or_default] + pub key: Option, + + #[prop_or_default] + pub children: Vec, + + /// CSS class of the container. + #[prop_or_default] + pub class: Classes, + + /// CSS style for the dialog window + #[prop_or_default] + pub styles: CssStyles, +} + +impl ContentSpacer { + pub fn new() -> Self { + yew::props!(Self {}) + } + + /// Builder style method to set the yew `key` property. + pub fn key(mut self, key: impl IntoOptionalKey) -> Self { + self.key = key.into_optional_key(); + self + } + + /// Builder style method to add a html class. + pub fn class(mut self, class: impl Into) -> Self { + self.add_class(class); + self + } + + /// Method to add a html class. + pub fn add_class(&mut self, class: impl Into) { + self.class.push(class); + } +} + +impl ContainerBuilder for ContentSpacer { + fn as_children_mut(&mut self) -> &mut Vec { + &mut self.children + } +} + +impl AsClassesMut for ContentSpacer { + fn as_classes_mut(&mut self) -> &mut yew::Classes { + &mut self.class + } +} + +impl AsCssStylesMut for ContentSpacer { + fn as_css_styles_mut(&mut self) -> &mut CssStyles { + &mut self.styles + } +} + +pub struct ProxmoxContentSpacer {} + +impl Component for ProxmoxContentSpacer { + type Message = (); + type Properties = ContentSpacer; + + fn create(_ctx: &Context) -> Self { + Self {} + } + + fn view(&self, ctx: &Context) -> Html { + let props = ctx.props(); + Container::new() + .class("proxmox-content-spacer") + .class((props.children.len() < 2).then(|| "proxmox-content-spacer-with-one-child")) + .class(props.class.clone()) + .styles(props.styles.clone()) + .children(props.children.clone()) + .into() + } +} + +impl From for VNode { + fn from(val: ContentSpacer) -> Self { + let key = val.key.clone(); + let comp = VComp::new::(Rc::new(val), key); + VNode::from(comp) + } +} diff --git a/ui/src/widget/mod.rs b/ui/src/widget/mod.rs index b885d1b..1104b01 100644 --- a/ui/src/widget/mod.rs +++ b/ui/src/widget/mod.rs @@ -1,3 +1,6 @@ +mod content_spacer; +pub use content_spacer::ContentSpacer; + mod migrate_window; pub use migrate_window::MigrateWindow; -- 2.39.5 _______________________________________________ pdm-devel mailing list pdm-devel@lists.proxmox.com https://lists.proxmox.com/cgi-bin/mailman/listinfo/pdm-devel