···11import formz
22import formz/field.{field}
33-import formz_string/definitions
44-import formz_string/widgets
33+import formz_string/definition
44+import formz_string/widget
5566pub fn make_form() {
77- use a <- formz.optional(field("text"), definitions.text_field())
88- use b <- formz.optional(field("int"), definitions.integer_field())
99- use c <- formz.optional(field("number"), definitions.number_field())
1010- use d <- formz.optional(field("bool"), definitions.boolean_field())
1111- use e <- formz.optional(field("email"), definitions.email_field())
1212- use f <- formz.optional(field("password"), definitions.password_field())
77+ use a <- formz.optional(field("text"), definition.text_field())
88+ use b <- formz.optional(field("int"), definition.integer_field())
99+ use c <- formz.optional(field("number"), definition.number_field())
1010+ use d <- formz.optional(field("bool"), definition.boolean_field())
1111+ use e <- formz.optional(field("email"), definition.email_field())
1212+ use f <- formz.optional(field("password"), definition.password_field())
1313 use g <- formz.optional(
1414 field("choices"),
1515- definitions.choices_field(letters(), A),
1515+ definition.choices_field(letters(), A),
1616 )
1717 use h <- formz.optional(
1818 field("list"),
1919- definitions.list_field(["Dog", "Cat", "Ant"]),
1919+ definition.list_field(["Dog", "Cat", "Ant"]),
2020 )
2121 use i <- formz.optional(
2222 field("textarea_widget"),
2323- definitions.text_field()
2424- |> formz.widget(widgets.textarea_widget()),
2323+ definition.text_field()
2424+ |> formz.widget(widget.textarea_widget()),
2525 )
26262727 formz.create_form(#(a, b, c, d, e, f, g, h, i))
···11import formz
22import formz/field.{field}
33-import formz_string/definitions
33+import formz_string/definition
4455pub fn make_form() {
66- use name <- formz.require(field(named: "name"), is: definitions.text_field())
66+ use name <- formz.require(field(named: "name"), is: definition.text_field())
77 use age <- formz.require(
88 field("age") |> field.set_label("Age"),
99- is: definitions.integer_field(),
99+ is: definition.integer_field(),
1010 )
1111 use height <- formz.require(
1212 field("height")
1313 |> field.set_label("Height (cm)")
1414 |> field.set_help_text("Please enter your height in centimeters"),
1515- is: definitions.integer_field(),
1515+ is: definition.integer_field(),
1616 )
17171818 formz.create_form(#(name, age, height))
···11import formz
22import formz/field.{field}
33-import formz_string/definitions
44-import formz_string/widgets
33+import formz_string/definition
44+import formz_string/widget
5566pub fn make_form() {
77- use a <- formz.require(field("text"), definitions.text_field())
88- use b <- formz.require(field("int"), definitions.integer_field())
99- use c <- formz.require(field("number"), definitions.number_field())
1010- use d <- formz.require(field("bool"), definitions.boolean_field())
1111- use e <- formz.require(field("email"), definitions.email_field())
1212- use f <- formz.require(field("password"), definitions.password_field())
77+ use a <- formz.require(field("text"), definition.text_field())
88+ use b <- formz.require(field("int"), definition.integer_field())
99+ use c <- formz.require(field("number"), definition.number_field())
1010+ use d <- formz.require(field("bool"), definition.boolean_field())
1111+ use e <- formz.require(field("email"), definition.email_field())
1212+ use f <- formz.require(field("password"), definition.password_field())
1313 use g <- formz.require(
1414 field("choices"),
1515- definitions.choices_field(letters(), stub: A),
1515+ definition.choices_field(letters(), stub: A),
1616 )
1717 use h <- formz.require(
1818 field("list"),
1919- definitions.list_field(["Dog", "Cat", "Ant"]),
1919+ definition.list_field(["Dog", "Cat", "Ant"]),
2020 )
2121 use i <- formz.require(
2222 field("textarea_widget"),
2323- definitions.text_field()
2424- |> formz.widget(widgets.textarea_widget()),
2323+ definition.text_field()
2424+ |> formz.widget(widget.textarea_widget()),
2525 )
26262727 formz.create_form(#(a, b, c, d, e, f, g, h, i))
+5-5
formz_demo/src/formz_demo/examples/sub_form.gleam
···11import formz
22import formz/field.{field}
33import formz/subform.{subform}
44-import formz_string/definitions
44+import formz_string/definition
5566pub fn make_form() {
77 use billing_address <- formz.subform(subform("billing"), address_form())
···1111}
12121313fn address_form() {
1414- use street <- formz.require(field("street"), definitions.text_field())
1515- use city <- formz.require(field("city"), definitions.text_field())
1414+ use street <- formz.require(field("street"), definition.text_field())
1515+ use city <- formz.require(field("city"), definition.text_field())
1616 use state <- formz.require(
1717 field("state"),
1818- definitions.list_field(states_list()),
1818+ definition.list_field(states_list()),
1919 )
2020 use postal_code <- formz.require(
2121 field.field("postal_code"),
2222- definitions.text_field(),
2222+ definition.text_field(),
2323 )
24242525 formz.create_form(Address(street:, city:, state:, postal_code:))
···6677import formz
88import formz/validation
99-import formz_lustre/widgets
99+import formz_lustre/widget
1010import gleam/int
1111import gleam/list
12121313/// Create a basic form input. Parsed as a String.
1414pub fn text_field() {
1515 formz.definition_with_custom_optional(
1616- widgets.input_widget("text"),
1616+ widget.input_widget("text"),
1717 validation.non_empty_string,
1818 "",
1919 fn(fun, str) {
···2929/// Create an email form input. Parsed as a String but must
3030/// look like an email address, i.e. the string has an `@`.
3131pub fn email_field() {
3232- formz.definition(widgets.input_widget("email"), validation.email, "")
3232+ formz.definition(widget.input_widget("email"), validation.email, "")
3333}
34343535/// Create a whole number form input. Parsed as an Int.
3636pub fn integer_field() {
3737- formz.definition(widgets.number_widget(""), validation.int, 0)
3737+ formz.definition(widget.number_widget(""), validation.int, 0)
3838}
39394040/// Create a number form input. Parsed as a Float.
4141pub fn number_field() {
4242- formz.definition(widgets.number_widget("0.01"), validation.number, 0.0)
4242+ formz.definition(widget.number_widget("0.01"), validation.number, 0.0)
4343}
44444545/// Create a checkbox form input. Parsed as a Boolean.
4646pub fn boolean_field() {
4747 formz.definition_with_custom_optional(
4848- widget: widgets.checkbox_widget(),
4848+ widget: widget.checkbox_widget(),
4949 parse: validation.on,
5050 stub: False,
5151 optional_parse: fn(parse, str) {
···60606161/// Create a password form input, which hides the input value. Parsed as a String
6262pub fn password_field() {
6363- formz.definition(widgets.password_widget(), validation.non_empty_string, "")
6363+ formz.definition(widget.password_widget(), validation.non_empty_string, "")
6464}
65656666/// Creates a `<select>` input. Takes a tuple of `#(String, String)` where the first
···7777 let values = variants |> list.map(fn(t) { t.1 })
78787979 formz.definition(
8080- widgets.select_widget(keys_indexed),
8080+ widget.select_widget(keys_indexed),
8181 validation.list_item_by_index(values)
8282 |> validation.replace_error("is required"),
8383 stub,
+215-1
formz_lustre/src/formz_lustre/widget.gleam
···99//// generators, and it's use is optional if you have different needs.
10101111import formz
1212-import formz/field
1212+import formz/field.{type Field}
1313+import gleam/list
1414+import gleam/string
1515+import lustre/attribute
1316import lustre/element
1717+import lustre/element/html
14181519pub type Widget(msg) {
1620 Widget(fn(field.Field, formz.InputState, Args) -> element.Element(msg))
···4549 DescribedByElementsWithIds(ids: List(String))
4650 DescribedByNone
4751}
5252+5353+fn id_attr(id: String) -> attribute.Attribute(msg) {
5454+ case id {
5555+ "" -> attribute.none()
5656+ _ -> attribute.id(id)
5757+ }
5858+}
5959+6060+fn name_attr(name: String) -> attribute.Attribute(msg) {
6161+ case name {
6262+ "" -> attribute.none()
6363+ _ -> attribute.name(name)
6464+ }
6565+}
6666+6767+fn aria_label_attr(
6868+ labelled_by: LabelledBy,
6969+ label: String,
7070+) -> attribute.Attribute(msg) {
7171+ case labelled_by {
7272+ LabelledByLabelFor -> attribute.none()
7373+ LabelledByElementsWithIds(ids) ->
7474+ attribute.attribute("aria-labelledby", string.join(ids, " "))
7575+ LabelledByFieldValue ->
7676+ case label {
7777+ "" -> attribute.none()
7878+ _ -> attribute.attribute("aria-label", label)
7979+ }
8080+ }
8181+}
8282+8383+fn aria_describedby_attr(described_by: DescribedBy) -> attribute.Attribute(msg) {
8484+ case described_by {
8585+ DescribedByNone -> attribute.none()
8686+ DescribedByElementsWithIds(ids) -> {
8787+ case ids |> list.filter(fn(x) { !string.is_empty(x) }) {
8888+ [] -> attribute.none()
8989+ non_empty_ids ->
9090+ attribute.attribute(
9191+ "aria-describedby",
9292+ string.join(non_empty_ids, " "),
9393+ )
9494+ }
9595+ }
9696+ }
9797+}
9898+9999+fn value_attr(value: String) -> attribute.Attribute(msg) {
100100+ case value {
101101+ "" -> attribute.none()
102102+ _ -> attribute.value(value)
103103+ }
104104+}
105105+106106+fn required_attr(requirement: formz.Requirement) -> attribute.Attribute(msg) {
107107+ case requirement {
108108+ formz.Required -> attribute.required(True)
109109+ formz.Optional -> attribute.none()
110110+ }
111111+}
112112+113113+fn step_size_attr(step_size: String) -> attribute.Attribute(msg) {
114114+ case step_size {
115115+ "" -> attribute.none()
116116+ _ -> attribute.attribute("step", step_size)
117117+ }
118118+}
119119+120120+fn checked_attr(value: String) -> attribute.Attribute(msg) {
121121+ case value {
122122+ "on" -> attribute.checked(True)
123123+ _ -> attribute.none()
124124+ }
125125+}
126126+127127+fn disabled_attr(disabled: Bool) -> attribute.Attribute(msg) {
128128+ case disabled {
129129+ True -> attribute.disabled(True)
130130+ False -> attribute.none()
131131+ }
132132+}
133133+134134+/// Create an `<input type="checkbox">`. The checkbox is checked
135135+/// if the value is "on" (the browser default).
136136+pub fn checkbox_widget() {
137137+ Widget(fn(field: Field, state: formz.InputState, args: Args) {
138138+ let value = state.value
139139+ let state = case state {
140140+ formz.Unvalidated(_, requirement) -> formz.Unvalidated("", requirement)
141141+ formz.Valid(_, requirement) -> formz.Valid("", requirement)
142142+ formz.Invalid(_, requirement, e) -> formz.Invalid("", requirement, e)
143143+ }
144144+ do_input_widget(field, state, args, "checkbox", [checked_attr(value)])
145145+ })
146146+}
147147+148148+/// Create a `<input type="number">`. Normally browsers only allow whole numbers,
149149+/// unless a decimal step size is provided. The step size here is a string that
150150+/// will be put straight into the `step-size` attribute. Doing non-whole numbers
151151+/// this way does mean that a user can only input numbers up to the precision of
152152+/// the step size. If you truly need any float, then a `type="text"` input might be a
153153+/// better choice.
154154+pub fn number_widget(step_size: String) {
155155+ Widget(fn(field: Field, state: formz.InputState, args: Args) {
156156+ do_input_widget(field, state, args, "number", [step_size_attr(step_size)])
157157+ })
158158+}
159159+160160+/// Create an `<input type="password">`. This will not output the value in the
161161+/// generated HTML for privacy/security concerns.
162162+pub fn password_widget() {
163163+ Widget(fn(field: Field, state: formz.InputState, args: Args) {
164164+ let state = case state {
165165+ formz.Unvalidated(_, requirement) -> formz.Unvalidated("", requirement)
166166+ formz.Valid(_, requirement) -> formz.Valid("", requirement)
167167+ formz.Invalid(_, requirement, e) -> formz.Invalid("", requirement, e)
168168+ }
169169+ do_input_widget(field, state, args, "password", [])
170170+ })
171171+}
172172+173173+/// Generate any `<input>` like `type="text"`, `type="email"` or
174174+/// `type="url"`.
175175+pub fn input_widget(type_: String) {
176176+ Widget(fn(field: Field, state: formz.InputState, args: Args) {
177177+ do_input_widget(field, state, args, type_, [])
178178+ })
179179+}
180180+181181+fn do_input_widget(
182182+ field: Field,
183183+ state: formz.InputState,
184184+ args: Args,
185185+ type_: String,
186186+ extra_attrs: List(attribute.Attribute(msg)),
187187+) {
188188+ html.input(
189189+ list.flatten([
190190+ [
191191+ attribute.type_(type_),
192192+ name_attr(field.name),
193193+ id_attr(args.id),
194194+ required_attr(state.requirement),
195195+ disabled_attr(field.disabled),
196196+ value_attr(state.value),
197197+ aria_label_attr(args.labelled_by, field.label),
198198+ aria_describedby_attr(args.described_by),
199199+ ],
200200+ extra_attrs,
201201+ ]),
202202+ )
203203+}
204204+205205+/// Create a `<textarea></textarea>`.
206206+pub fn textarea_widget() {
207207+ Widget(
208208+ fn(field: Field, state: formz.InputState, args: Args) -> element.Element(
209209+ msg,
210210+ ) {
211211+ html.textarea(
212212+ [
213213+ name_attr(field.name),
214214+ id_attr(args.id),
215215+ required_attr(state.requirement),
216216+ aria_label_attr(args.labelled_by, field.label),
217217+ aria_describedby_attr(args.described_by),
218218+ ],
219219+ state.value,
220220+ )
221221+ },
222222+ )
223223+}
224224+225225+/// Create a `<input type="hidden">`. This is useful for if a field is just
226226+/// passing data around and you don't want it to be visible to the user. Like
227227+/// say, the ID of a record being edited.
228228+pub fn hidden_widget() {
229229+ Hidden
230230+}
231231+232232+/// Create a `<select></select>` with `<option>`s for each variant. The list
233233+/// of variants is a two-tuple, where the first item is the text to display and
234234+/// the second item is the value.
235235+pub fn select_widget(variants: List(#(String, String))) {
236236+ Widget(
237237+ fn(field: Field, state: formz.InputState, args: Args) -> element.Element(
238238+ msg,
239239+ ) {
240240+ html.select(
241241+ [
242242+ name_attr(field.name),
243243+ id_attr(args.id),
244244+ required_attr(state.requirement),
245245+ aria_label_attr(args.labelled_by, field.label),
246246+ aria_describedby_attr(args.described_by),
247247+ ],
248248+ list.flatten([
249249+ [html.option([attribute.value("")], "Select..."), html.hr([])],
250250+ list.map(variants, fn(variant) {
251251+ let val = variant.1
252252+ html.option(
253253+ [attribute.value(val), attribute.selected(state.value == val)],
254254+ variant.0,
255255+ )
256256+ }),
257257+ ]),
258258+ )
259259+ },
260260+ )
261261+}
-220
formz_lustre/src/formz_lustre/widgets.gleam
···11-import formz
22-import formz/field.{type Field}
33-import formz_lustre/widget
44-import gleam/list
55-import gleam/string
66-import lustre/attribute
77-import lustre/element
88-import lustre/element/html
99-1010-fn id_attr(id: String) -> attribute.Attribute(msg) {
1111- case id {
1212- "" -> attribute.none()
1313- _ -> attribute.id(id)
1414- }
1515-}
1616-1717-fn name_attr(name: String) -> attribute.Attribute(msg) {
1818- case name {
1919- "" -> attribute.none()
2020- _ -> attribute.name(name)
2121- }
2222-}
2323-2424-fn aria_label_attr(
2525- labelled_by: widget.LabelledBy,
2626- label: String,
2727-) -> attribute.Attribute(msg) {
2828- case labelled_by {
2929- widget.LabelledByLabelFor -> attribute.none()
3030- widget.LabelledByElementsWithIds(ids) ->
3131- attribute.attribute("aria-labelledby", string.join(ids, " "))
3232- widget.LabelledByFieldValue ->
3333- case label {
3434- "" -> attribute.none()
3535- _ -> attribute.attribute("aria-label", label)
3636- }
3737- }
3838-}
3939-4040-fn aria_describedby_attr(
4141- described_by: widget.DescribedBy,
4242-) -> attribute.Attribute(msg) {
4343- case described_by {
4444- widget.DescribedByNone -> attribute.none()
4545- widget.DescribedByElementsWithIds(ids) -> {
4646- case ids |> list.filter(fn(x) { !string.is_empty(x) }) {
4747- [] -> attribute.none()
4848- non_empty_ids ->
4949- attribute.attribute(
5050- "aria-describedby",
5151- string.join(non_empty_ids, " "),
5252- )
5353- }
5454- }
5555- }
5656-}
5757-5858-fn value_attr(value: String) -> attribute.Attribute(msg) {
5959- case value {
6060- "" -> attribute.none()
6161- _ -> attribute.value(value)
6262- }
6363-}
6464-6565-fn required_attr(requirement: formz.Requirement) -> attribute.Attribute(msg) {
6666- case requirement {
6767- formz.Required -> attribute.required(True)
6868- formz.Optional -> attribute.none()
6969- }
7070-}
7171-7272-fn step_size_attr(step_size: String) -> attribute.Attribute(msg) {
7373- case step_size {
7474- "" -> attribute.none()
7575- _ -> attribute.attribute("step", step_size)
7676- }
7777-}
7878-7979-fn checked_attr(value: String) -> attribute.Attribute(msg) {
8080- case value {
8181- "on" -> attribute.checked(True)
8282- _ -> attribute.none()
8383- }
8484-}
8585-8686-fn disabled_attr(disabled: Bool) -> attribute.Attribute(msg) {
8787- case disabled {
8888- True -> attribute.disabled(True)
8989- False -> attribute.none()
9090- }
9191-}
9292-9393-/// Create an `<input type="checkbox">`. The checkbox is checked
9494-/// if the value is "on" (the browser default).
9595-pub fn checkbox_widget() {
9696- widget.Widget(fn(field: Field, state: formz.InputState, args: widget.Args) {
9797- let value = state.value
9898- let state = case state {
9999- formz.Unvalidated(_, requirement) -> formz.Unvalidated("", requirement)
100100- formz.Valid(_, requirement) -> formz.Valid("", requirement)
101101- formz.Invalid(_, requirement, e) -> formz.Invalid("", requirement, e)
102102- }
103103- do_input_widget(field, state, args, "checkbox", [checked_attr(value)])
104104- })
105105-}
106106-107107-/// Create a `<input type="number">`. Normally browsers only allow whole numbers,
108108-/// unless a decimal step size is provided. The step size here is a string that
109109-/// will be put straight into the `step-size` attribute. Doing non-whole numbers
110110-/// this way does mean that a user can only input numbers up to the precision of
111111-/// the step size. If you truly need any float, then a `type="text"` input might be a
112112-/// better choice.
113113-pub fn number_widget(step_size: String) {
114114- widget.Widget(fn(field: Field, state: formz.InputState, args: widget.Args) {
115115- do_input_widget(field, state, args, "number", [step_size_attr(step_size)])
116116- })
117117-}
118118-119119-/// Create an `<input type="password">`. This will not output the value in the
120120-/// generated HTML for privacy/security concerns.
121121-pub fn password_widget() {
122122- widget.Widget(fn(field: Field, state: formz.InputState, args: widget.Args) {
123123- let state = case state {
124124- formz.Unvalidated(_, requirement) -> formz.Unvalidated("", requirement)
125125- formz.Valid(_, requirement) -> formz.Valid("", requirement)
126126- formz.Invalid(_, requirement, e) -> formz.Invalid("", requirement, e)
127127- }
128128- do_input_widget(field, state, args, "password", [])
129129- })
130130-}
131131-132132-/// Generate any `<input>` like `type="text"`, `type="email"` or
133133-/// `type="url"`.
134134-pub fn input_widget(type_: String) {
135135- widget.Widget(fn(field: Field, state: formz.InputState, args: widget.Args) {
136136- do_input_widget(field, state, args, type_, [])
137137- })
138138-}
139139-140140-fn do_input_widget(
141141- field: Field,
142142- state: formz.InputState,
143143- args: widget.Args,
144144- type_: String,
145145- extra_attrs: List(attribute.Attribute(msg)),
146146-) {
147147- html.input(
148148- list.flatten([
149149- [
150150- attribute.type_(type_),
151151- name_attr(field.name),
152152- id_attr(args.id),
153153- required_attr(state.requirement),
154154- disabled_attr(field.disabled),
155155- value_attr(state.value),
156156- aria_label_attr(args.labelled_by, field.label),
157157- aria_describedby_attr(args.described_by),
158158- ],
159159- extra_attrs,
160160- ]),
161161- )
162162-}
163163-164164-/// Create a `<textarea></textarea>`.
165165-pub fn textarea_widget() {
166166- widget.Widget(
167167- fn(field: Field, state: formz.InputState, args: widget.Args) -> element.Element(
168168- msg,
169169- ) {
170170- html.textarea(
171171- [
172172- name_attr(field.name),
173173- id_attr(args.id),
174174- required_attr(state.requirement),
175175- aria_label_attr(args.labelled_by, field.label),
176176- aria_describedby_attr(args.described_by),
177177- ],
178178- state.value,
179179- )
180180- },
181181- )
182182-}
183183-184184-/// Create a `<input type="hidden">`. This is useful for if a field is just
185185-/// passing data around and you don't want it to be visible to the user. Like
186186-/// say, the ID of a record being edited.
187187-pub fn hidden_widget() {
188188- widget.Hidden
189189-}
190190-191191-/// Create a `<select></select>` with `<option>`s for each variant. The list
192192-/// of variants is a two-tuple, where the first item is the text to display and
193193-/// the second item is the value.
194194-pub fn select_widget(variants: List(#(String, String))) {
195195- widget.Widget(
196196- fn(field: Field, state: formz.InputState, args: widget.Args) -> element.Element(
197197- msg,
198198- ) {
199199- html.select(
200200- [
201201- name_attr(field.name),
202202- id_attr(args.id),
203203- required_attr(state.requirement),
204204- aria_label_attr(args.labelled_by, field.label),
205205- aria_describedby_attr(args.described_by),
206206- ],
207207- list.flatten([
208208- [html.option([attribute.value("")], "Select..."), html.hr([])],
209209- list.map(variants, fn(variant) {
210210- let val = variant.1
211211- html.option(
212212- [attribute.value(val), attribute.selected(state.value == val)],
213213- variant.0,
214214- )
215215- }),
216216- ]),
217217- )
218218- },
219219- )
220220-}
+16-16
formz_lustre/test/formz_lustre/fields_test.gleam
···11-import formz_lustre/definitions
11+import formz_lustre/definition
2233import formz.{type Definition}
44-import formz_string/definitions as string_definitions
44+import formz_string/definition as string_definition
55import gleeunit
66import gleeunit/should
77···1616}
17171818pub fn text_field_test() {
1919- let string_definition = string_definitions.text_field()
2020- let definition = definitions.text_field()
1919+ let string_definition = string_definition.text_field()
2020+ let definition = definition.text_field()
21212222 compare_parse_fns(definition, string_definition, "")
2323 compare_parse_fns(definition, string_definition, "a")
2424}
25252626pub fn email_field_test() {
2727- let string_definition = string_definitions.email_field()
2828- let definition = definitions.email_field()
2727+ let string_definition = string_definition.email_field()
2828+ let definition = definition.email_field()
29293030 compare_parse_fns(definition, string_definition, "")
3131 compare_parse_fns(definition, string_definition, "a")
3232}
33333434pub fn number_field_test() {
3535- let string_definition = string_definitions.number_field()
3636- let definition = definitions.number_field()
3535+ let string_definition = string_definition.number_field()
3636+ let definition = definition.number_field()
37373838 compare_parse_fns(definition, string_definition, "")
3939 compare_parse_fns(definition, string_definition, "a")
4040}
41414242pub fn integer_field_test() {
4343- let string_definition = string_definitions.integer_field()
4444- let definition = definitions.integer_field()
4343+ let string_definition = string_definition.integer_field()
4444+ let definition = definition.integer_field()
45454646 compare_parse_fns(definition, string_definition, "")
4747 compare_parse_fns(definition, string_definition, "a")
4848}
49495050pub fn boolean_field_test() {
5151- let string_definition = string_definitions.boolean_field()
5252- let definition = definitions.boolean_field()
5151+ let string_definition = string_definition.boolean_field()
5252+ let definition = definition.boolean_field()
53535454 compare_parse_fns(definition, string_definition, "")
5555 compare_parse_fns(definition, string_definition, "a")
···57575858pub fn choices_field_test() {
5959 let string_definition =
6060- string_definitions.choices_field([#("a", "A"), #("b", "B")], "")
6161- let definition = definitions.choices_field([#("a", "A"), #("b", "B")], "")
6060+ string_definition.choices_field([#("a", "A"), #("b", "B")], "")
6161+ let definition = definition.choices_field([#("a", "A"), #("b", "B")], "")
62626363 compare_parse_fns(definition, string_definition, "")
6464 compare_parse_fns(definition, string_definition, "a")
6565}
66666767pub fn indexed_enum_field_test() {
6868- let string_definition = string_definitions.list_field(["A", "B"])
6969- let definition = definitions.list_field(["A", "B"])
6868+ let string_definition = string_definition.list_field(["A", "B"])
6969+ let definition = definition.list_field(["A", "B"])
70707171 compare_parse_fns(definition, string_definition, "")
7272 compare_parse_fns(definition, string_definition, "a")
+10-10
formz_lustre/test/formz_lustre/simple_test.gleam
···11import formz
22import formz/field.{field}
33import formz/subform
44-import formz_lustre/definitions
44+import formz_lustre/definition
55import formz_lustre/simple
66-import formz_string/definitions as string_definitions
66+import formz_string/definition as string_definition
77import formz_string/simple as string_simple
88import gleeunit
99import gleeunit/should
···1919}
20202121pub fn three_field_form() {
2222- use a <- formz.require(field("a"), definitions.integer_field())
2323- use b <- formz.require(field("b"), definitions.integer_field())
2424- use c <- formz.optional(field("c"), definitions.integer_field())
2222+ use a <- formz.require(field("a"), definition.integer_field())
2323+ use b <- formz.require(field("b"), definition.integer_field())
2424+ use c <- formz.optional(field("c"), definition.integer_field())
25252626 formz.create_form(#(a, b, c))
2727}
28282929pub fn three_field_string_form() {
3030- use a <- formz.require(field("a"), string_definitions.integer_field())
3131- use b <- formz.require(field("b"), string_definitions.integer_field())
3232- use c <- formz.optional(field("c"), string_definitions.integer_field())
3030+ use a <- formz.require(field("a"), string_definition.integer_field())
3131+ use b <- formz.require(field("b"), string_definition.integer_field())
3232+ use c <- formz.optional(field("c"), string_definition.integer_field())
33333434 formz.create_form(#(a, b, c))
3535}
36363737pub fn one_field_and_subform_form() {
3838- use a <- formz.require(field("a"), definitions.integer_field())
3838+ use a <- formz.require(field("a"), definition.integer_field())
3939 use b <- formz.subform(subform.subform("b"), three_field_form())
40404141 formz.create_form(#(a, b))
4242}
43434444pub fn one_field_and_subform_string_form() {
4545- use a <- formz.require(field("a"), string_definitions.integer_field())
4545+ use a <- formz.require(field("a"), string_definition.integer_field())
4646 use b <- formz.subform(subform.subform("b"), three_field_string_form())
47474848 formz.create_form(#(a, b))
···6677import formz
88import formz/validation
99-import formz_nakai/widgets
99+import formz_nakai/widget
1010import gleam/int
1111import gleam/list
12121313/// Create a basic form input. Parsed as a String.
1414pub fn text_field() {
1515 formz.definition_with_custom_optional(
1616- widgets.input_widget("text"),
1616+ widget.input_widget("text"),
1717 validation.non_empty_string,
1818 "",
1919 fn(parse, str) {
···2929/// Create an email form input. Parsed as a String but must
3030/// look like an email address, i.e. the string has an `@`.
3131pub fn email_field() {
3232- formz.definition(widgets.input_widget("email"), validation.email, "")
3232+ formz.definition(widget.input_widget("email"), validation.email, "")
3333}
34343535/// Create a whole number form input. Parsed as an Int.
3636pub fn integer_field() {
3737- formz.definition(widgets.number_widget(""), validation.int, 0)
3737+ formz.definition(widget.number_widget(""), validation.int, 0)
3838}
39394040/// Create a number form input. Parsed as a Float.
4141pub fn number_field() {
4242- formz.definition(widgets.number_widget("0.01"), validation.number, 0.0)
4242+ formz.definition(widget.number_widget("0.01"), validation.number, 0.0)
4343}
44444545/// Create a checkbox form input. Parsed as a Boolean.
4646pub fn boolean_field() {
4747 formz.definition_with_custom_optional(
4848- widget: widgets.checkbox_widget(),
4848+ widget: widget.checkbox_widget(),
4949 parse: validation.on,
5050 stub: False,
5151 optional_parse: fn(parse, str) {
···60606161/// Create a password form input, which hides the input value. Parsed as a String
6262pub fn password_field() {
6363- formz.definition(widgets.password_widget(), validation.non_empty_string, "")
6363+ formz.definition(widget.password_widget(), validation.non_empty_string, "")
6464}
65656666/// Creates a `<select>` input. Takes a tuple of `#(String, String)` where the first
···7777 let values = variants |> list.map(fn(t) { t.1 })
78787979 formz.definition(
8080- widgets.select_widget(keys_indexed),
8080+ widget.select_widget(keys_indexed),
8181 validation.list_item_by_index(values)
8282 |> validation.replace_error("is required"),
8383 stub,
+204-1
formz_nakai/src/formz_nakai/widget.gleam
···99//// generators, and it's use is optional if you have different needs.
10101111import formz
1212-import formz/field
1212+import formz/field.{type Field, Field}
1313+import gleam/list
1414+import gleam/string
1515+import nakai/attr
1316import nakai/html
14171518pub type Widget {
···4548 DescribedByElementsWithIds(ids: List(String))
4649 DescribedByNone
4750}
5151+5252+fn id_attr(id: String) -> List(attr.Attr) {
5353+ case id {
5454+ "" -> []
5555+ _ -> [attr.id(id)]
5656+ }
5757+}
5858+5959+fn name_attr(name: String) -> List(attr.Attr) {
6060+ case name {
6161+ "" -> []
6262+ _ -> [attr.name(name)]
6363+ }
6464+}
6565+6666+fn aria_label_attr(labelled_by: LabelledBy, label: String) -> List(attr.Attr) {
6767+ case labelled_by {
6868+ LabelledByLabelFor -> []
6969+ LabelledByElementsWithIds(ids) -> [
7070+ attr.aria_labelledby(string.join(ids, " ")),
7171+ ]
7272+ LabelledByFieldValue ->
7373+ case label {
7474+ "" -> []
7575+ _ -> [attr.aria_label(label)]
7676+ }
7777+ }
7878+}
7979+8080+fn aria_describedby_attr(described_by: DescribedBy) -> List(attr.Attr) {
8181+ case described_by {
8282+ DescribedByNone -> []
8383+ DescribedByElementsWithIds(ids) ->
8484+ case ids |> list.filter(fn(x) { !string.is_empty(x) }) {
8585+ [] -> []
8686+ non_empty_ids -> [
8787+ attr.Attr("aria-describedby", string.join(non_empty_ids, " ")),
8888+ ]
8989+ }
9090+ }
9191+}
9292+9393+fn type_attr(type_: String) -> List(attr.Attr) {
9494+ [attr.type_(type_)]
9595+}
9696+9797+fn value_attr(value: String) -> List(attr.Attr) {
9898+ case value {
9999+ "" -> []
100100+ _ -> [attr.value(value)]
101101+ }
102102+}
103103+104104+fn required_attr(requirement: formz.Requirement) -> List(attr.Attr) {
105105+ case requirement {
106106+ formz.Required -> [attr.required("")]
107107+ formz.Optional -> []
108108+ }
109109+}
110110+111111+fn checked_attr(value: String) -> List(attr.Attr) {
112112+ case value {
113113+ "on" -> [attr.checked()]
114114+ _ -> []
115115+ }
116116+}
117117+118118+fn disabled_attr(disabled: Bool) -> List(attr.Attr) {
119119+ case disabled {
120120+ True -> [attr.disabled()]
121121+ False -> []
122122+ }
123123+}
124124+125125+fn step_size_attr(step_size: String) -> List(attr.Attr) {
126126+ case step_size {
127127+ "" -> []
128128+ _ -> [attr.Attr("step", step_size)]
129129+ }
130130+}
131131+132132+/// Create an `<input type="checkbox">`. The checkbox is checked
133133+/// if the value is "on" (the browser default).
134134+pub fn checkbox_widget() -> Widget {
135135+ Widget(fn(field: Field, state: formz.InputState, args: Args) {
136136+ let value = state.value
137137+ let state = case state {
138138+ formz.Unvalidated(_, requirement) -> formz.Unvalidated("", requirement)
139139+ formz.Valid(_, requirement) -> formz.Valid("", requirement)
140140+ formz.Invalid(_, requirement, e) -> formz.Invalid("", requirement, e)
141141+ }
142142+ do_input_widget(field, state, args, "checkbox", [checked_attr(value)])
143143+ })
144144+}
145145+146146+/// Create a `<input type="number">`. Normally browsers only allow whole numbers,
147147+/// unless a decimal step size is provided. The step size here is a string that
148148+/// will be put straight into the `step-size` attribute. Doing non-whole numbers
149149+/// this way does mean that a user can only input numbers up to the precision of
150150+/// the step size. If you truly need any float, then a `type="text"` input might be a
151151+/// better choice.
152152+pub fn number_widget(step_size: String) {
153153+ Widget(fn(field: Field, state: formz.InputState, args: Args) {
154154+ do_input_widget(field, state, args, "number", [step_size_attr(step_size)])
155155+ })
156156+}
157157+158158+/// Create an `<input type="password">`. This will not output the value in the
159159+/// generated HTML for privacy/security concerns.
160160+pub fn password_widget() {
161161+ Widget(fn(field: Field, state: formz.InputState, args: Args) {
162162+ let state = case state {
163163+ formz.Unvalidated(_, requirement) -> formz.Unvalidated("", requirement)
164164+ formz.Valid(_, requirement) -> formz.Valid("", requirement)
165165+ formz.Invalid(_, requirement, e) -> formz.Invalid("", requirement, e)
166166+ }
167167+ do_input_widget(field, state, args, "password", [])
168168+ })
169169+}
170170+171171+/// Generate any `<input>` like `type="text"`, `type="email"` or
172172+/// `type="url"`.
173173+pub fn input_widget(type_: String) {
174174+ Widget(fn(field: Field, state: formz.InputState, args: Args) {
175175+ do_input_widget(field, state, args, type_, [])
176176+ })
177177+}
178178+179179+fn do_input_widget(
180180+ field: Field,
181181+ state: formz.InputState,
182182+ args: Args,
183183+ type_: String,
184184+ extra_attrs: List(List(attr.Attr)),
185185+) {
186186+ html.input(
187187+ list.flatten([
188188+ type_attr(type_),
189189+ name_attr(field.name),
190190+ id_attr(args.id),
191191+ required_attr(state.requirement),
192192+ value_attr(state.value),
193193+ disabled_attr(field.disabled),
194194+ aria_describedby_attr(args.described_by),
195195+ aria_label_attr(args.labelled_by, field.label),
196196+ extra_attrs |> list.flatten,
197197+ ]),
198198+ )
199199+}
200200+201201+/// Create a `<textarea></textarea>`.
202202+pub fn textarea_widget() {
203203+ Widget(fn(field: Field, state: formz.InputState, args: Args) -> html.Node {
204204+ html.textarea(
205205+ list.flatten([
206206+ name_attr(field.name),
207207+ id_attr(args.id),
208208+ required_attr(state.requirement),
209209+ aria_label_attr(args.labelled_by, field.label),
210210+ ]),
211211+ [html.Text(state.value)],
212212+ )
213213+ })
214214+}
215215+216216+/// Create a `<input type="hidden">`. This is useful for if a field is just
217217+/// passing data around and you don't want it to be visible to the user. Like
218218+/// say, the ID of a record being edited.
219219+pub fn hidden_widget() {
220220+ Hidden
221221+}
222222+223223+/// Create a `<select></select>` with `<option>`s for each variant. The list
224224+/// of variants is a two-tuple, where the first item is the text to display and
225225+/// the second item is the value.
226226+pub fn select_widget(variants: List(#(String, String))) {
227227+ Widget(fn(field: Field, state: formz.InputState, args: Args) -> html.Node {
228228+ html.select(
229229+ list.flatten([
230230+ name_attr(field.name),
231231+ id_attr(args.id),
232232+ required_attr(state.requirement),
233233+ aria_label_attr(args.labelled_by, field.label),
234234+ ]),
235235+ list.flatten([
236236+ [html.option([attr.value("")], [html.Text("Select...")]), html.hr([])],
237237+ list.map(variants, fn(variant) {
238238+ let val = variant.1
239239+ let selected_attr = case state.value == val {
240240+ True -> [attr.selected()]
241241+ _ -> []
242242+ }
243243+ html.option(list.flatten([value_attr(val), selected_attr]), [
244244+ html.Text(variant.0),
245245+ ])
246246+ }),
247247+ ]),
248248+ )
249249+ })
250250+}
-215
formz_nakai/src/formz_nakai/widgets.gleam
···11-import formz
22-import formz/field.{type Field}
33-import formz_nakai/widget
44-import gleam/string
55-66-import gleam/list
77-import nakai/attr
88-import nakai/html
99-1010-fn id_attr(id: String) -> List(attr.Attr) {
1111- case id {
1212- "" -> []
1313- _ -> [attr.id(id)]
1414- }
1515-}
1616-1717-fn name_attr(name: String) -> List(attr.Attr) {
1818- case name {
1919- "" -> []
2020- _ -> [attr.name(name)]
2121- }
2222-}
2323-2424-fn aria_label_attr(
2525- labelled_by: widget.LabelledBy,
2626- label: String,
2727-) -> List(attr.Attr) {
2828- case labelled_by {
2929- widget.LabelledByLabelFor -> []
3030- widget.LabelledByElementsWithIds(ids) -> [
3131- attr.aria_labelledby(string.join(ids, " ")),
3232- ]
3333- widget.LabelledByFieldValue ->
3434- case label {
3535- "" -> []
3636- _ -> [attr.aria_label(label)]
3737- }
3838- }
3939-}
4040-4141-fn aria_describedby_attr(described_by: widget.DescribedBy) -> List(attr.Attr) {
4242- case described_by {
4343- widget.DescribedByNone -> []
4444- widget.DescribedByElementsWithIds(ids) ->
4545- case ids |> list.filter(fn(x) { !string.is_empty(x) }) {
4646- [] -> []
4747- non_empty_ids -> [
4848- attr.Attr("aria-describedby", string.join(non_empty_ids, " ")),
4949- ]
5050- }
5151- }
5252-}
5353-5454-fn type_attr(type_: String) -> List(attr.Attr) {
5555- [attr.type_(type_)]
5656-}
5757-5858-fn value_attr(value: String) -> List(attr.Attr) {
5959- case value {
6060- "" -> []
6161- _ -> [attr.value(value)]
6262- }
6363-}
6464-6565-fn required_attr(requirement: formz.Requirement) -> List(attr.Attr) {
6666- case requirement {
6767- formz.Required -> [attr.required("")]
6868- formz.Optional -> []
6969- }
7070-}
7171-7272-fn checked_attr(value: String) -> List(attr.Attr) {
7373- case value {
7474- "on" -> [attr.checked()]
7575- _ -> []
7676- }
7777-}
7878-7979-fn disabled_attr(disabled: Bool) -> List(attr.Attr) {
8080- case disabled {
8181- True -> [attr.disabled()]
8282- False -> []
8383- }
8484-}
8585-8686-fn step_size_attr(step_size: String) -> List(attr.Attr) {
8787- case step_size {
8888- "" -> []
8989- _ -> [attr.Attr("step", step_size)]
9090- }
9191-}
9292-9393-/// Create an `<input type="checkbox">`. The checkbox is checked
9494-/// if the value is "on" (the browser default).
9595-pub fn checkbox_widget() -> widget.Widget {
9696- widget.Widget(fn(field: Field, state: formz.InputState, args: widget.Args) {
9797- let value = state.value
9898- let state = case state {
9999- formz.Unvalidated(_, requirement) -> formz.Unvalidated("", requirement)
100100- formz.Valid(_, requirement) -> formz.Valid("", requirement)
101101- formz.Invalid(_, requirement, e) -> formz.Invalid("", requirement, e)
102102- }
103103- do_input_widget(field, state, args, "checkbox", [checked_attr(value)])
104104- })
105105-}
106106-107107-/// Create a `<input type="number">`. Normally browsers only allow whole numbers,
108108-/// unless a decimal step size is provided. The step size here is a string that
109109-/// will be put straight into the `step-size` attribute. Doing non-whole numbers
110110-/// this way does mean that a user can only input numbers up to the precision of
111111-/// the step size. If you truly need any float, then a `type="text"` input might be a
112112-/// better choice.
113113-pub fn number_widget(step_size: String) {
114114- widget.Widget(fn(field: Field, state: formz.InputState, args: widget.Args) {
115115- do_input_widget(field, state, args, "number", [step_size_attr(step_size)])
116116- })
117117-}
118118-119119-/// Create an `<input type="password">`. This will not output the value in the
120120-/// generated HTML for privacy/security concerns.
121121-pub fn password_widget() {
122122- widget.Widget(fn(field: Field, state: formz.InputState, args: widget.Args) {
123123- let state = case state {
124124- formz.Unvalidated(_, requirement) -> formz.Unvalidated("", requirement)
125125- formz.Valid(_, requirement) -> formz.Valid("", requirement)
126126- formz.Invalid(_, requirement, e) -> formz.Invalid("", requirement, e)
127127- }
128128- do_input_widget(field, state, args, "password", [])
129129- })
130130-}
131131-132132-/// Generate any `<input>` like `type="text"`, `type="email"` or
133133-/// `type="url"`.
134134-pub fn input_widget(type_: String) {
135135- widget.Widget(fn(field: Field, state: formz.InputState, args: widget.Args) {
136136- do_input_widget(field, state, args, type_, [])
137137- })
138138-}
139139-140140-fn do_input_widget(
141141- field: Field,
142142- state: formz.InputState,
143143- args: widget.Args,
144144- type_: String,
145145- extra_attrs: List(List(attr.Attr)),
146146-) {
147147- html.input(
148148- list.flatten([
149149- type_attr(type_),
150150- name_attr(field.name),
151151- id_attr(args.id),
152152- required_attr(state.requirement),
153153- value_attr(state.value),
154154- disabled_attr(field.disabled),
155155- aria_describedby_attr(args.described_by),
156156- aria_label_attr(args.labelled_by, field.label),
157157- extra_attrs |> list.flatten,
158158- ]),
159159- )
160160-}
161161-162162-/// Create a `<textarea></textarea>`.
163163-pub fn textarea_widget() {
164164- widget.Widget(
165165- fn(field: Field, state: formz.InputState, args: widget.Args) -> html.Node {
166166- html.textarea(
167167- list.flatten([
168168- name_attr(field.name),
169169- id_attr(args.id),
170170- required_attr(state.requirement),
171171- aria_label_attr(args.labelled_by, field.label),
172172- ]),
173173- [html.Text(state.value)],
174174- )
175175- },
176176- )
177177-}
178178-179179-/// Create a `<input type="hidden">`. This is useful for if a field is just
180180-/// passing data around and you don't want it to be visible to the user. Like
181181-/// say, the ID of a record being edited.
182182-pub fn hidden_widget() {
183183- widget.Hidden
184184-}
185185-186186-/// Create a `<select></select>` with `<option>`s for each variant. The list
187187-/// of variants is a two-tuple, where the first item is the text to display and
188188-/// the second item is the value.
189189-pub fn select_widget(variants: List(#(String, String))) {
190190- widget.Widget(
191191- fn(field: Field, state: formz.InputState, args: widget.Args) -> html.Node {
192192- html.select(
193193- list.flatten([
194194- name_attr(field.name),
195195- id_attr(args.id),
196196- required_attr(state.requirement),
197197- aria_label_attr(args.labelled_by, field.label),
198198- ]),
199199- list.flatten([
200200- [html.option([attr.value("")], [html.Text("Select...")]), html.hr([])],
201201- list.map(variants, fn(variant) {
202202- let val = variant.1
203203- let selected_attr = case state.value == val {
204204- True -> [attr.selected()]
205205- _ -> []
206206- }
207207- html.option(list.flatten([value_attr(val), selected_attr]), [
208208- html.Text(variant.0),
209209- ])
210210- }),
211211- ]),
212212- )
213213- },
214214- )
215215-}
+16-16
formz_nakai/test/formz_nakai/fields_test.gleam
···11-import formz_nakai/definitions
11+import formz_nakai/definition
2233import formz.{type Definition}
44-import formz_string/definitions as string_definitions
44+import formz_string/definition as string_definition
55import gleeunit
66import gleeunit/should
77···1616}
17171818pub fn text_field_test() {
1919- let string_definition = string_definitions.text_field()
2020- let definition = definitions.text_field()
1919+ let string_definition = string_definition.text_field()
2020+ let definition = definition.text_field()
21212222 compare_parse_fns(definition, string_definition, "")
2323 compare_parse_fns(definition, string_definition, "a")
2424}
25252626pub fn email_field_test() {
2727- let string_definition = string_definitions.email_field()
2828- let definition = definitions.email_field()
2727+ let string_definition = string_definition.email_field()
2828+ let definition = definition.email_field()
29293030 compare_parse_fns(definition, string_definition, "")
3131 compare_parse_fns(definition, string_definition, "a")
3232}
33333434pub fn number_field_test() {
3535- let string_definition = string_definitions.number_field()
3636- let definition = definitions.number_field()
3535+ let string_definition = string_definition.number_field()
3636+ let definition = definition.number_field()
37373838 compare_parse_fns(definition, string_definition, "")
3939 compare_parse_fns(definition, string_definition, "a")
4040}
41414242pub fn integer_field_test() {
4343- let string_definition = string_definitions.integer_field()
4444- let definition = definitions.integer_field()
4343+ let string_definition = string_definition.integer_field()
4444+ let definition = definition.integer_field()
45454646 compare_parse_fns(definition, string_definition, "")
4747 compare_parse_fns(definition, string_definition, "a")
4848}
49495050pub fn boolean_field_test() {
5151- let string_definition = string_definitions.boolean_field()
5252- let definition = definitions.boolean_field()
5151+ let string_definition = string_definition.boolean_field()
5252+ let definition = definition.boolean_field()
53535454 compare_parse_fns(definition, string_definition, "")
5555 compare_parse_fns(definition, string_definition, "a")
···57575858pub fn choices_field_test() {
5959 let string_definition =
6060- string_definitions.choices_field([#("a", "A"), #("b", "B")], "")
6161- let definition = definitions.choices_field([#("a", "A"), #("b", "B")], "")
6060+ string_definition.choices_field([#("a", "A"), #("b", "B")], "")
6161+ let definition = definition.choices_field([#("a", "A"), #("b", "B")], "")
62626363 compare_parse_fns(definition, string_definition, "")
6464 compare_parse_fns(definition, string_definition, "a")
6565}
66666767pub fn indexed_enum_field_test() {
6868- let string_definition = string_definitions.list_field(["A", "B"])
6969- let definition = definitions.list_field(["A", "B"])
6868+ let string_definition = string_definition.list_field(["A", "B"])
6969+ let definition = definition.list_field(["A", "B"])
70707171 compare_parse_fns(definition, string_definition, "")
7272 compare_parse_fns(definition, string_definition, "a")
+10-10
formz_nakai/test/formz_nakai/simple_test.gleam
···11import formz
22import formz/field.{field}
33import formz/subform
44-import formz_nakai/definitions
44+import formz_nakai/definition
55import formz_nakai/simple
66-import formz_string/definitions as string_definitions
66+import formz_string/definition as string_definition
77import formz_string/simple as string_simple
88import gleam/string
99import gleeunit
···3737}
38383939pub fn three_field_form() {
4040- use a <- formz.require(field("a"), definitions.integer_field())
4141- use b <- formz.require(field("b"), definitions.integer_field())
4242- use c <- formz.optional(field("c"), definitions.integer_field())
4040+ use a <- formz.require(field("a"), definition.integer_field())
4141+ use b <- formz.require(field("b"), definition.integer_field())
4242+ use c <- formz.optional(field("c"), definition.integer_field())
43434444 formz.create_form(#(a, b, c))
4545}
46464747pub fn three_field_string_form() {
4848- use a <- formz.require(field("a"), string_definitions.integer_field())
4949- use b <- formz.require(field("b"), string_definitions.integer_field())
5050- use c <- formz.optional(field("c"), string_definitions.integer_field())
4848+ use a <- formz.require(field("a"), string_definition.integer_field())
4949+ use b <- formz.require(field("b"), string_definition.integer_field())
5050+ use c <- formz.optional(field("c"), string_definition.integer_field())
51515252 formz.create_form(#(a, b, c))
5353}
54545555pub fn one_field_and_subform_form() {
5656- use a <- formz.require(field("a"), definitions.integer_field())
5656+ use a <- formz.require(field("a"), definition.integer_field())
5757 use b <- formz.subform(subform.subform("b"), three_field_form())
58585959 formz.create_form(#(a, b))
6060}
61616262pub fn one_field_and_subform_string_form() {
6363- use a <- formz.require(field("a"), string_definitions.integer_field())
6363+ use a <- formz.require(field("a"), string_definition.integer_field())
6464 use b <- formz.subform(subform.subform("b"), three_field_string_form())
65656666 formz.create_form(#(a, b))