all lists on lists.proxmox.com
 help / color / mirror / Atom feed
From: Hannes Laimer <h.laimer@proxmox.com>
To: yew-devel@lists.proxmox.com
Subject: [yew-devel] [PATCH yew-widget-toolkit v2 2/2] form: add schema() builder funtion to checkbox, number and field
Date: Thu,  9 Oct 2025 13:25:02 +0200	[thread overview]
Message-ID: <20251009112502.312917-3-h.laimer@proxmox.com> (raw)
In-Reply-To: <20251009112502.312917-1-h.laimer@proxmox.com>

This will use the passed schema to populate properties of the form
field depending on what type of schema is passed. The idea is to only
have things like validation, help texts/description or selection items
specified once. Namely in the Schema definition.

These .schema() builder functions are guarded by the proxmox-schema
feature.

Signed-off-by: Hannes Laimer <h.laimer@proxmox.com>
---
 Cargo.toml                  |  5 +++
 src/widget/form/checkbox.rs | 20 +++++++++++
 src/widget/form/combobox.rs | 45 +++++++++++++++---------
 src/widget/form/field.rs    | 28 +++++++++++++++
 src/widget/form/number.rs   | 69 ++++++++++++++++++++++++++++++++++---
 5 files changed, 146 insertions(+), 21 deletions(-)

diff --git a/Cargo.toml b/Cargo.toml
index 86f73cc..b2d2f1c 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -71,4 +71,9 @@ url = "2.1"
 percent-encoding = "2.1"
 gettext = "0.4"
 
+proxmox-schema = { version = "5.0.0", optional = true }
 pwt-macros = { version = "0.5", path = "pwt-macros" }
+
+[features]
+default = ["proxmox-schema"]
+proxmox-schema = ["dep:proxmox-schema"]
diff --git a/src/widget/form/checkbox.rs b/src/widget/form/checkbox.rs
index 6b2cf4a..833cd44 100644
--- a/src/widget/form/checkbox.rs
+++ b/src/widget/form/checkbox.rs
@@ -4,6 +4,9 @@ use serde_json::Value;
 use yew::html::{IntoEventCallback, IntoPropValue};
 use yew::prelude::*;
 
+#[cfg(feature = "proxmox-schema")]
+use proxmox_schema::Schema;
+
 use pwt_macros::{builder, widget};
 
 use super::{
@@ -107,6 +110,23 @@ impl Checkbox {
         yew::props!(Self {})
     }
 
+    /// Builder style method to set the schema, will set tooltip and default value
+    #[cfg(feature = "proxmox-schema")]
+    pub fn schema(mut self, schema: &'static Schema) -> Self {
+        self.set_schema(schema);
+        self
+    }
+
+    #[cfg(feature = "proxmox-schema")]
+    pub fn set_schema(&mut self, schema: &'static Schema) {
+        if let proxmox_schema::Schema::Boolean(s) = schema {
+            self.set_tip(s.description);
+            if let Some(value) = s.default {
+                self.set_default(value);
+            }
+        }
+    }
+
     /// Builder style method to set the validate callback
     pub fn validate(mut self, validate: impl IntoValidateFn<bool>) -> Self {
         self.set_validate(validate);
diff --git a/src/widget/form/combobox.rs b/src/widget/form/combobox.rs
index db4505f..208df53 100644
--- a/src/widget/form/combobox.rs
+++ b/src/widget/form/combobox.rs
@@ -7,8 +7,8 @@ use yew::html::{IntoEventCallback, IntoPropValue};
 use yew::prelude::*;
 use yew::virtual_dom::Key;
 
-// #[cfg(feature = "proxmox-schema")]
-// use proxmox_schema::Schema;
+#[cfg(feature = "proxmox-schema")]
+use proxmox_schema::Schema;
 
 use crate::prelude::*;
 use crate::props::{IntoOptionalRenderFn, IntoTextFilterFn, RenderFn, TextFilterFn};
@@ -172,21 +172,32 @@ impl Combobox {
         self.validate = validate.into_validate_fn();
     }
 
-    // /// Builder style method to set the validation schema
-    // #[cfg(feature = "proxmox-schema")]
-    // pub fn schema(mut self, schema: &'static Schema) -> Self {
-    //     self.set_schema(schema);
-    //     self
-    // }
-
-    // /// Method to set the validation schema
-    // #[cfg(feature = "proxmox-schema")]
-    // pub fn set_schema(&mut self, schema: &'static Schema) {
-    //     self.set_validate(move |(value, _store): &(String, _)| {
-    //         schema.parse_simple_value(value)?;
-    //         Ok(())
-    //     });
-    // }
+    /// Builder style method to set the schema, will set validation, items and placeholder
+    #[cfg(feature = "proxmox-schema")]
+    pub fn schema(mut self, schema: &'static Schema) -> Self {
+        self.set_schema(schema);
+        self
+    }
+
+    #[cfg(feature = "proxmox-schema")]
+    pub fn set_schema(&mut self, schema: &'static Schema) {
+        if let proxmox_schema::Schema::String(s) = schema {
+            if let Some(proxmox_schema::ApiStringFormat::Enum(e)) = s.format {
+                let items: Vec<AttrValue> = e
+                    .iter()
+                    .map(|entry| entry.value.to_string().into())
+                    .collect();
+                self.set_items(items.into());
+            }
+            if let Some(value) = s.default {
+                self.set_placeholder(value);
+            }
+        }
+        self.set_validate(move |(value, _store): &(String, _)| {
+            schema.parse_simple_value(value)?;
+            Ok(())
+        });
+    }
 
     /// Builder style method to add an trigger
     pub fn with_trigger(mut self, trigger: impl Into<Trigger>, right: bool) -> Self {
diff --git a/src/widget/form/field.rs b/src/widget/form/field.rs
index 4a37d12..4b69a94 100644
--- a/src/widget/form/field.rs
+++ b/src/widget/form/field.rs
@@ -8,12 +8,16 @@ use web_sys::HtmlInputElement;
 use yew::html::{IntoEventCallback, IntoPropValue};
 use yew::prelude::*;
 
+#[cfg(feature = "proxmox-schema")]
+use proxmox_schema::Schema;
+
 use pwt_macros::{builder, widget};
 
 use super::{
     IntoValidateFn, ManagedField, ManagedFieldContext, ManagedFieldMaster, ManagedFieldState,
     ValidateFn,
 };
+use crate::props::FieldBuilder as _;
 use crate::props::{
     ContainerBuilder, EventSubscriber, IntoVTag, WidgetBuilder, WidgetStyleBuilder,
 };
@@ -234,6 +238,30 @@ impl Field {
         self
     }
 
+    /// Builder style method to set the schema, will set the placeholder, tooltip and validation
+    #[cfg(feature = "proxmox-schema")]
+    pub fn schema(mut self, schema: &'static Schema) -> Self {
+        self.set_schema(schema);
+        self
+    }
+
+    #[cfg(feature = "proxmox-schema")]
+    pub fn set_schema(&mut self, schema: &'static proxmox_schema::Schema) {
+        if let proxmox_schema::Schema::String(s) = schema {
+            // if s.format is a pattern we could include the regex, may be helpful occationally,
+            // but not sure. For things like IPs for example we really don't want that...
+            self.set_tip(s.description.to_string());
+            if let Some(default) = s.default {
+                self.set_placeholder(default.to_string());
+            }
+        }
+
+        self.set_validate(move |value: &String| {
+            schema.parse_simple_value(value)?;
+            Ok(())
+        });
+    }
+
     /// Builder style method to set the validate callback
     pub fn validate(mut self, validate: impl IntoValidateFn<String>) -> Self {
         self.set_validate(validate);
diff --git a/src/widget/form/number.rs b/src/widget/form/number.rs
index ad1a3cd..33c5d93 100644
--- a/src/widget/form/number.rs
+++ b/src/widget/form/number.rs
@@ -10,14 +10,16 @@ use web_sys::HtmlInputElement;
 use yew::html::{IntoEventCallback, IntoPropValue};
 use yew::prelude::*;
 
-use pwt_macros::{builder, widget};
+#[cfg(feature = "proxmox-schema")]
+use proxmox_schema::Schema;
 
 use super::{
     IntoValidateFn, ManagedField, ManagedFieldContext, ManagedFieldMaster, ManagedFieldState,
     ValidateFn,
 };
-use crate::props::{ContainerBuilder, EventSubscriber, IntoVTag, WidgetBuilder};
+use crate::props::{ContainerBuilder, EventSubscriber, FieldBuilder as _, IntoVTag, WidgetBuilder};
 use crate::widget::{Column, Container, Input, Tooltip};
+use pwt_macros::{builder, widget};
 
 use crate::tr;
 
@@ -397,6 +399,58 @@ impl<T: NumberTypeInfo> Number<T> {
     pub fn set_validate(&mut self, validate: impl IntoValidateFn<T>) {
         self.validate = validate.into_validate_fn();
     }
+
+    /// Builder style method to set the schema, will set min, max, default and validation
+    #[cfg(feature = "proxmox-schema")]
+    pub fn schema(mut self, schema: &'static Schema) -> Self {
+        self.set_schema(schema);
+        self
+    }
+
+    /// Method to set the validation schema, min, max, default and placeholder
+    #[cfg(feature = "proxmox-schema")]
+    pub fn set_schema(&mut self, schema: &'static Schema) {
+        if let proxmox_schema::Schema::Integer(s) = schema {
+            self.set_tip(s.description);
+            if let Some(minimum) = s.minimum {
+                if let Ok(min_value) = T::value_to_number(&Value::Number(minimum.into())) {
+                    self.min = Some(min_value);
+                }
+            }
+            if let Some(maximum) = s.maximum {
+                if let Ok(max_value) = T::value_to_number(&Value::Number(maximum.into())) {
+                    self.max = Some(max_value);
+                }
+            }
+
+            if let Some(default) = s.default {
+                self.set_placeholder(default.to_string());
+            }
+        }
+
+        if let proxmox_schema::Schema::Number(s) = schema {
+            self.set_tip(s.description);
+            if let Some(minimum) = s.minimum {
+                if let Ok(min_value) = T::value_to_number(&Value::from(minimum)) {
+                    self.min = Some(min_value);
+                }
+            }
+            if let Some(maximum) = s.maximum {
+                if let Ok(max_value) = T::value_to_number(&Value::from(maximum)) {
+                    self.max = Some(max_value);
+                }
+            }
+            if let Some(default) = s.default {
+                self.set_placeholder(default.to_string());
+            }
+        }
+
+        self.set_validate(move |value: &T| {
+            let value_str = value.to_string();
+            schema.parse_simple_value(&value_str)?;
+            Ok(())
+        });
+    }
 }
 
 pub enum Msg {
@@ -711,8 +765,15 @@ impl<T: NumberTypeInfo> ManagedField for NumberField<T> {
                     ),
             );
 
-        if let Err(msg) = &validation_result {
-            input_container.set_tip(msg.clone())
+        match validation_result {
+            Err(msg) => input_container.set_tip(msg.clone()),
+            Ok(_) => {
+                if let Some(tip) = &props.tip {
+                    if !disabled {
+                        input_container.set_tip(tip.clone())
+                    }
+                }
+            }
         }
 
         input_container.into()
-- 
2.47.3



_______________________________________________
yew-devel mailing list
yew-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/yew-devel


  parent reply	other threads:[~2025-10-09 11:25 UTC|newest]

Thread overview: 7+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2025-10-09 11:25 [yew-devel] [PATCH yew-widget-toolkit v2 0/2] use Schema to populate field properties Hannes Laimer
2025-10-09 11:25 ` [yew-devel] [PATCH yew-widget-toolkit v2 1/2] number: add tooltip to number field Hannes Laimer
2025-10-09 11:51   ` Dominik Csapak
2025-10-09 11:54     ` Hannes Laimer
2025-10-09 11:25 ` Hannes Laimer [this message]
2025-10-09 11:30 ` [yew-devel] [PATCH yew-widget-toolkit v2 0/2] use Schema to populate field properties Dietmar Maurer
2025-10-09 13:32 ` [yew-devel] superseded: " Hannes Laimer

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20251009112502.312917-3-h.laimer@proxmox.com \
    --to=h.laimer@proxmox.com \
    --cc=yew-devel@lists.proxmox.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.
Service provided by Proxmox Server Solutions GmbH | Privacy | Legal