tablo_core/schema/fields/
custom.rs1use tablo_ui::checkbox as ui_checkbox;
5use topcoat::{context::Cx, view::*};
6
7pub trait Control: Send + Sync {
30 fn render<'a>(&self, cx: &'a Cx, input: ControlInput) -> BoxView<'a>;
31}
32
33#[derive(Debug, Clone)]
36pub struct ControlInput {
37 name: String,
38 id: String,
39 value: Option<String>,
40 required: bool,
41 invalid: bool,
42 described_by: Option<String>,
43 disabled: bool,
44}
45
46impl ControlInput {
47 pub(crate) fn new(
48 name: &str,
49 id: &str,
50 value: Option<&str>,
51 required: bool,
52 invalid: bool,
53 described_by: Option<String>,
54 ) -> Self {
55 Self {
56 name: name.to_string(),
57 id: id.to_string(),
58 value: value.map(str::to_string),
59 required,
60 invalid,
61 described_by,
62 disabled: false,
63 }
64 }
65
66 pub(crate) fn disabled(mut self, disabled: bool) -> Self {
67 self.disabled = disabled;
68 self
69 }
70
71 pub fn name(&self) -> &str {
72 &self.name
73 }
74
75 pub fn id(&self) -> &str {
78 &self.id
79 }
80
81 pub fn value(&self) -> Option<&str> {
82 self.value.as_deref()
83 }
84
85 pub fn required(&self) -> bool {
86 self.required
87 }
88
89 pub fn invalid(&self) -> bool {
90 self.invalid
91 }
92
93 pub fn described_by(&self) -> Option<&str> {
94 self.described_by.as_deref()
95 }
96
97 pub fn is_disabled(&self) -> bool {
99 self.disabled
100 }
101
102 pub fn attributes(&self, cx: &Cx) -> Attributes {
103 let required = self.required;
104 attributes! {
105 cx =>
106 id=(self.id.clone())
107 name=(self.name.clone())
108 value=(self.value.clone())
109 required=(required)
110 disabled=(self.disabled)
111 aria-required=(required.then_some("true"))
112 aria-invalid=(if self.invalid { "true" } else { "false" })
113 aria-describedby=(self.described_by.clone())
114 }
115 }
116}
117
118#[derive(Debug, Clone, Copy, Default)]
120pub struct Toggle;
121
122impl Control for Toggle {
123 fn render<'a>(&self, cx: &'a Cx, input: ControlInput) -> BoxView<'a> {
124 let name = input.name().to_string();
125 let id = input.id().to_string();
126 let checked = input.value().is_some_and(|v| v.trim() == "true");
127 let invalid = if input.invalid() { "true" } else { "false" };
128 let described_by = input.described_by().map(str::to_string);
129 let disabled = input.is_disabled();
130 view! {
131 cx =>
132 <input type="hidden" name=(name.clone()) value="false" disabled=(disabled)>
133 ui_checkbox(
134 attrs: attributes! {
135 id=(id)
136 name=(name)
137 value="true"
138 checked=(checked)
139 disabled=(disabled)
140 aria-invalid=(invalid)
141 aria-describedby=(described_by)
142 }
143 )
144 }
145 .boxed()
146 }
147}