Skip to main content

tablo_core/schema/fields/builders/
availability.rs

1//! The modifiers saying when a control renders disabled or not at all, beyond `disabled` and
2//! `disabled_on_edit`: on one of a record form's two forms, or while another field posts a value.
3
4use super::{ChoiceField, CustomField, FileField, TextField};
5use crate::schema::condition::{Condition, Watched};
6#[cfg(feature = "rich-text")]
7use crate::schema::fields::RichTextField;
8
9/// `hidden_on_create` and `hidden_on_edit`, on the builders of a record form's controls.
10macro_rules! form_visibility {
11    ($builder:ident) => {
12        impl<F: crate::schema::EditsRecords> $builder<F> {
13            /// Renders nothing for the field on the create form. The create stores the field's
14            /// `default`, else its blank answer: a required field with neither cannot mount. A
15            /// field hidden on both forms only ever stores that: set it in the record instead.
16            pub fn hidden_on_create(mut self) -> Self {
17                self.0.hidden.create = true;
18                self
19            }
20
21            /// Renders nothing for the field on the edit form, and keeps its stored value whatever
22            /// an edit posts: the create form sets the value, and no edit changes it, as
23            /// [`disabled_on_edit`](Self::disabled_on_edit) does without showing it.
24            pub fn hidden_on_edit(mut self) -> Self {
25                self.0.hidden.edit = true;
26                self
27            }
28        }
29    };
30}
31
32/// `disabled_when`, on the builders whose control a disabled fieldset disables: a rich text
33/// editor reads its state once, as it starts.
34macro_rules! conditional_disable {
35    ($builder:ident) => {
36        impl<F> $builder<F> {
37            /// Renders the control disabled while `watched` posts one of `values`: an option's
38            /// value, or `true` or `false` for a toggle. Disabled, the control posts nothing, so
39            /// an edit keeps the stored value and a create takes the field's `default`, else its
40            /// blank answer.
41            ///
42            /// ```rust
43            /// # #[derive(Debug, Clone, toasty::Model)]
44            /// # struct Order {
45            /// #     #[key] #[auto] id: uuid::Uuid,
46            /// #     status: String,
47            /// #     total: i64,
48            /// # }
49            /// # use tablo_core::{Field, Schema};
50            /// let status = Field::choice(Order::fields().status()).options(["draft", "sent"]);
51            /// let total = Field::text(Order::fields().total()).disabled_when(&status, ["sent"]);
52            /// Schema::new((status, total));
53            /// ```
54            ///
55            /// - The browser enables and disables the control as the watched field changes, with no
56            ///   request, through a disabled fieldset around it. An app's own
57            ///   [`Control`](crate::extend::Control) follows it only through the form controls it
58            ///   renders. Without JavaScript, the control starts as the form's initial values set
59            ///   it, the stored ones on an edit, and stays that way.
60            /// - The panel reads a submission the same way and drops the field's posted key, and a
61            ///   file field's carried upload, while the condition holds. The condition reads what
62            ///   the user may post, so it is no access check:
63            ///   [`ResourceDef::field_access`](crate::ResourceDef::field_access) is.
64            /// - Mounting refuses what it refuses for [`visible_when`](Self::visible_when): an
65            ///   unplaced watched field, a value it never posts, a multiple choice, and a watched
66            ///   field another condition hides while this one still shows. It also refuses a
67            ///   required field with no non-blank `default`, which a create could not fill while
68            ///   the condition holds, and a condition or a dependent choice watching this field: a
69            ///   disabled control posts nothing, so the browser and the server would read different
70            ///   values.
71            /// - A later call replaces an earlier one.
72            pub fn disabled_when(
73                mut self,
74                watched: &impl Watched<F>,
75                values: impl IntoIterator<Item = impl Into<String>>,
76            ) -> Self {
77                self.0.disabling = Some(Condition::new(watched, values));
78                self
79            }
80        }
81    };
82}
83
84form_visibility!(TextField);
85form_visibility!(ChoiceField);
86form_visibility!(FileField);
87form_visibility!(CustomField);
88#[cfg(feature = "rich-text")]
89form_visibility!(RichTextField);
90conditional_disable!(TextField);
91conditional_disable!(ChoiceField);
92conditional_disable!(FileField);
93conditional_disable!(CustomField);