···11+import formz/definition
12import formz/field.{field}
23import formz/formz_use as formz
34import formz_string/definitions
55+import formz_string/widgets
4657pub fn make_form() {
66- let choices = [#("Yes", True), #("Maybe", True), #("No", False)]
77-88 use a <- formz.with(field("a"), definitions.text_field())
99 use b <- formz.with(field("b"), definitions.integer_field())
1010 use c <- formz.with(field("c"), definitions.number_field())
1111 use d <- formz.with(field("d"), definitions.boolean_field())
1212 use e <- formz.with(field("e"), definitions.email_field())
1313- use f <- formz.with(field("g"), definitions.enum_field(letters()))
1414- use g <- formz.with(field("h"), definitions.indexed_enum_field(choices))
1515- use h <- formz.with(field("i"), definitions.list_field(["Dog", "Cat", "Ant"]))
1313+ use f <- formz.with(field("f"), definitions.password_field())
1414+ use g <- formz.with(
1515+ field("g"),
1616+ definitions.choices_field(letters(), placeholder: A),
1717+ )
1818+ use h <- formz.with(field("h"), definitions.list_field(["Dog", "Cat", "Ant"]))
1919+ use i <- formz.with(
2020+ field("i"),
2121+ definitions.text_field()
2222+ |> definition.set_widget(widgets.textarea_widget()),
2323+ )
16241717- formz.create_form(#(a, b, c, d, e, f, g, h))
2525+ formz.create_form(#(a, b, c, d, e, f, g, h, i))
1826}
19272028pub type Alphabet {
+36-16
formz_lustre/src/formz_lustre/definitions.gleam
···11+//// Hopefully these are pretty self-explanatory. I haven't provided any
22+//// examples here, as these are all used to build HTML forms and I don't know
33+//// how helpful it would be to see the raw HTML. If you'd like to see
44+//// examples of these in action, please see the [formz_demo](https://github.com/bentomas/formz/tree/main/formz_demo)
55+//// example project.
66+17import formz/definition.{Definition}
28import formz/validation
39import formz_lustre/widgets
1010+import gleam/int
411import gleam/list
5121313+/// Create a basic form input. Parsed as a String.
614pub fn text_field() {
715 Definition(widgets.text_like_widget("text"), validation.string, "")
816}
9171818+/// Create an email form input. Parsed as a String but must
1919+/// look like an email address, i.e. the string has an `@`.
1020pub fn email_field() {
1121 Definition(widgets.text_like_widget("email"), validation.email, "")
1222}
13232424+/// Create a whole number form input. Parsed as an Int.
1425pub fn integer_field() {
1526 Definition(widgets.text_like_widget("number"), validation.int, 0)
1627}
17282929+/// Create a number form input. Parsed as a Float.
1830pub fn number_field() {
1931 Definition(widgets.text_like_widget("number"), validation.number, 0.0)
2032}
21333434+/// Create a checkbox form input. Parsed as a Boolean.
2235pub fn boolean_field() {
2336 Definition(widgets.checkbox_widget(), validation.boolean, False)
2437}
25383939+/// Create a password form input, which hides the input value. Parsed as a String
2640pub fn password_field() {
2741 Definition(widgets.password_widget(), validation.string, "")
2842}
29433030-pub fn enum_field(variants: List(#(String, enum))) {
3131- let assert Ok(#(_, first)) = list.first(variants)
3232- Definition(
3333- widgets.select_widget(variants),
3434- validation.enum(variants)
3535- |> validation.replace_error("Please select an option"),
3636- first,
3737- )
3838-}
4444+/// Creates a `<select>` input. Takes a tuple of #(String, String) where the first
4545+/// item in the tuple is the label, and the second item can be any Gleam type and
4646+/// is the value that would be parsed for a given selection.
4747+///
4848+/// Because of how you build `formz` forms, you need to provide a placeholder of
4949+/// the value type. Is this annoying? Would it be more or less annoying if I
5050+/// required a non-empty list for the variants instead? I'm not sure. Let me know!
5151+pub fn choices_field(
5252+ variants: List(#(String, enum)),
5353+ placeholder placeholder: enum,
5454+) {
5555+ let keys_indexed =
5656+ variants
5757+ |> list.index_map(fn(t, i) { #(t.0, int.to_string(i)) })
39584040-pub fn indexed_enum_field(variants: List(#(String, enum))) {
4141- let keys_indexed = list.index_map(variants, fn(t, i) { #(t.0, i) })
4242- let assert Ok(#(_, first)) = list.first(variants)
5959+ let values = variants |> list.map(fn(t) { t.1 })
6060+4361 Definition(
4462 widgets.select_widget(keys_indexed),
4545- validation.enum_by_index(variants)
6363+ validation.list_item_by_index(values)
4664 |> validation.replace_error("Please select an option"),
4747- first,
6565+ placeholder,
4866 )
4967}
50686969+/// Creates a `<select>` input from a list of strings. Validates that the parsed
7070+/// value is one of the strings in the list.
5171pub fn list_field(variants: List(String)) {
5252- let tuple_list = list.map(variants, fn(s) { #(s, s) })
5353- indexed_enum_field(tuple_list)
7272+ let labels_and_values = list.map(variants, fn(s) { #(s, s) })
7373+ choices_field(labels_and_values, "")
5474}
···11-import formz/definition
21import gleeunit
32import gleeunit/should
4354import formz_lustre/definitions
66-import formz_string/definitions as string_kinds
55+import formz_string/definitions as string_definitions
7687pub fn main() {
98 gleeunit.main()
109}
11101212-fn fields_should_be_equal_except_widget(
1313- field1: definition.Definition(format1, output),
1414- field2: definition.Definition(format2, output),
1515-) {
1616- field1.placeholder |> should.equal(field2.placeholder)
1717- field1.transform |> should.equal(field2.transform)
1818-}
1919-2011pub fn text_field_test() {
2121- let string_field = string_kinds.text_field()
1212+ let string_field = string_definitions.text_field()
2213 let field = definitions.text_field()
23142424- fields_should_be_equal_except_widget(field, string_field)
1515+ field.transform |> should.equal(string_field.transform)
2516}
26172718pub fn email_field_test() {
2828- let string_field = string_kinds.email_field()
1919+ let string_field = string_definitions.email_field()
2920 let field = definitions.email_field()
30213131- fields_should_be_equal_except_widget(field, string_field)
2222+ field.transform |> should.equal(string_field.transform)
3223}
33243425pub fn number_field_test() {
3535- let string_field = string_kinds.number_field()
2626+ let string_field = string_definitions.number_field()
3627 let field = definitions.number_field()
37283838- fields_should_be_equal_except_widget(field, string_field)
2929+ field.transform |> should.equal(string_field.transform)
3930}
40314132pub fn integer_field_test() {
4242- let string_field = string_kinds.integer_field()
3333+ let string_field = string_definitions.integer_field()
4334 let field = definitions.integer_field()
44354545- fields_should_be_equal_except_widget(field, string_field)
3636+ field.transform |> should.equal(string_field.transform)
4637}
47384839pub fn boolean_field_test() {
4949- let string_field = string_kinds.boolean_field()
4040+ let string_field = string_definitions.boolean_field()
5041 let field = definitions.boolean_field()
51425252- fields_should_be_equal_except_widget(field, string_field)
4343+ field.transform |> should.equal(string_field.transform)
5344}
54455555-pub fn enum_field_test() {
5656- let string_field = string_kinds.enum_field([#("a", "A"), #("b", "B")])
5757- let field = definitions.enum_field([#("a", "A"), #("b", "B")])
4646+pub fn choices_field_test() {
4747+ let string_field =
4848+ string_definitions.choices_field([#("a", "A"), #("b", "B")], "")
4949+ let field = definitions.choices_field([#("a", "A"), #("b", "B")], "")
58505959- fields_should_be_equal_except_widget(field, string_field)
5151+ field.transform |> should.equal(string_field.transform)
6052}
61536254pub fn indexed_enum_field_test() {
6363- let string_field = string_kinds.indexed_enum_field([#("a", "A"), #("b", "B")])
6464- let field = definitions.indexed_enum_field([#("a", "A"), #("b", "B")])
5555+ let string_field = string_definitions.list_field(["A", "B"])
5656+ let field = definitions.list_field(["A", "B"])
65576666- fields_should_be_equal_except_widget(field, string_field)
5858+ field.transform |> should.equal(string_field.transform)
6759}
+36-16
formz_nakai/src/formz_nakai/definitions.gleam
···11+//// Hopefully these are pretty self-explanatory. I haven't provided any
22+//// examples here, as these are all used to build HTML forms and I don't know
33+//// how helpful it would be to see the raw HTML. If you'd like to see
44+//// examples of these in action, please see the [formz_demo](https://github.com/bentomas/formz/tree/main/formz_demo)
55+//// example project.
66+17import formz/definition.{Definition}
28import formz/validation
39import formz_nakai/widgets
1010+import gleam/int
411import gleam/list
5121313+/// Create a basic form input. Parsed as a String.
614pub fn text_field() {
715 Definition(widgets.text_like_widget("text"), validation.string, "")
816}
9171818+/// Create an email form input. Parsed as a String but must
1919+/// look like an email address, i.e. the string has an `@`.
1020pub fn email_field() {
1121 Definition(widgets.text_like_widget("email"), validation.email, "")
1222}
13232424+/// Create a whole number form input. Parsed as an Int.
1425pub fn integer_field() {
1526 Definition(widgets.text_like_widget("number"), validation.int, 0)
1627}
17282929+/// Create a number form input. Parsed as a Float.
1830pub fn number_field() {
1931 Definition(widgets.text_like_widget("number"), validation.number, 0.0)
2032}
21333434+/// Create a checkbox form input. Parsed as a Boolean.
2235pub fn boolean_field() {
2336 Definition(widgets.checkbox_widget(), validation.boolean, False)
2437}
25383939+/// Create a password form input, which hides the input value. Parsed as a String
2640pub fn password_field() {
2741 Definition(widgets.password_widget(), validation.string, "")
2842}
29433030-pub fn enum_field(variants: List(#(String, enum))) {
3131- let assert Ok(#(_, first)) = list.first(variants)
3232- Definition(
3333- widgets.select_widget(variants),
3434- validation.enum(variants)
3535- |> validation.replace_error("Please select an option"),
3636- first,
3737- )
3838-}
4444+/// Creates a `<select>` input. Takes a tuple of #(String, String) where the first
4545+/// item in the tuple is the label, and the second item can be any Gleam type and
4646+/// is the value that would be parsed for a given selection.
4747+///
4848+/// Because of how you build `formz` forms, you need to provide a placeholder of
4949+/// the value type. Is this annoying? Would it be more or less annoying if I
5050+/// required a non-empty list for the variants instead? I'm not sure. Let me know!
5151+pub fn choices_field(
5252+ variants: List(#(String, enum)),
5353+ placeholder placeholder: enum,
5454+) {
5555+ let keys_indexed =
5656+ variants
5757+ |> list.index_map(fn(t, i) { #(t.0, int.to_string(i)) })
39584040-pub fn indexed_enum_field(variants: List(#(String, enum))) {
4141- let keys_indexed = list.index_map(variants, fn(t, i) { #(t.0, i) })
4242- let assert Ok(#(_, first)) = list.first(variants)
5959+ let values = variants |> list.map(fn(t) { t.1 })
6060+4361 Definition(
4462 widgets.select_widget(keys_indexed),
4545- validation.enum_by_index(variants)
6363+ validation.list_item_by_index(values)
4664 |> validation.replace_error("Please select an option"),
4747- first,
6565+ placeholder,
4866 )
4967}
50686969+/// Creates a `<select>` input from a list of strings. Validates that the parsed
7070+/// value is one of the strings in the list.
5171pub fn list_field(variants: List(String)) {
5252- let tuple_list = list.map(variants, fn(s) { #(s, s) })
5353- indexed_enum_field(tuple_list)
7272+ let labels_and_values = list.map(variants, fn(s) { #(s, s) })
7373+ choices_field(labels_and_values, "")
5474}
···11-import formz/definition
21import gleeunit
32import gleeunit/should
4354import formz_nakai/definitions
66-import formz_string/definitions as string_kinds
55+import formz_string/definitions as string_definitions
7687pub fn main() {
98 gleeunit.main()
109}
11101212-fn fields_should_be_equal_except_widget(
1313- field1: definition.Definition(format1, output),
1414- field2: definition.Definition(format2, output),
1515-) {
1616- field1.placeholder |> should.equal(field2.placeholder)
1717- field1.transform |> should.equal(field2.transform)
1818-}
1919-2011pub fn text_field_test() {
2121- let string_field = string_kinds.text_field()
1212+ let string_field = string_definitions.text_field()
2213 let field = definitions.text_field()
23142424- fields_should_be_equal_except_widget(field, string_field)
1515+ field.transform |> should.equal(string_field.transform)
2516}
26172718pub fn email_field_test() {
2828- let string_field = string_kinds.email_field()
1919+ let string_field = string_definitions.email_field()
2920 let field = definitions.email_field()
30213131- fields_should_be_equal_except_widget(field, string_field)
2222+ field.transform |> should.equal(string_field.transform)
3223}
33243425pub fn number_field_test() {
3535- let string_field = string_kinds.number_field()
2626+ let string_field = string_definitions.number_field()
3627 let field = definitions.number_field()
37283838- fields_should_be_equal_except_widget(field, string_field)
2929+ field.transform |> should.equal(string_field.transform)
3930}
40314132pub fn integer_field_test() {
4242- let string_field = string_kinds.integer_field()
3333+ let string_field = string_definitions.integer_field()
4334 let field = definitions.integer_field()
44354545- fields_should_be_equal_except_widget(field, string_field)
3636+ field.transform |> should.equal(string_field.transform)
4637}
47384839pub fn boolean_field_test() {
4949- let string_field = string_kinds.boolean_field()
4040+ let string_field = string_definitions.boolean_field()
5041 let field = definitions.boolean_field()
51425252- fields_should_be_equal_except_widget(field, string_field)
4343+ field.transform |> should.equal(string_field.transform)
5344}
54455555-pub fn enum_field_test() {
5656- let string_field = string_kinds.enum_field([#("a", "A"), #("b", "B")])
5757- let field = definitions.enum_field([#("a", "A"), #("b", "B")])
4646+pub fn choices_field_test() {
4747+ let string_field =
4848+ string_definitions.choices_field([#("a", "A"), #("b", "B")], "")
4949+ let field = definitions.choices_field([#("a", "A"), #("b", "B")], "")
58505959- fields_should_be_equal_except_widget(field, string_field)
5151+ field.transform |> should.equal(string_field.transform)
6052}
61536254pub fn indexed_enum_field_test() {
6363- let string_field = string_kinds.indexed_enum_field([#("a", "A"), #("b", "B")])
6464- let field = definitions.indexed_enum_field([#("a", "A"), #("b", "B")])
5555+ let string_field = string_definitions.list_field(["A", "B"])
5656+ let field = definitions.list_field(["A", "B"])
65576666- fields_should_be_equal_except_widget(field, string_field)
5858+ field.transform |> should.equal(string_field.transform)
6759}
+36-16
formz_string/src/formz_string/definitions.gleam
···11+//// Hopefully these are pretty self-explanatory. I haven't provided any
22+//// examples here, as these are all used to build HTML forms and I don't know
33+//// how helpful it would be to see the raw HTML. If you'd like to see
44+//// examples of these in action, please see the [formz_demo](https://github.com/bentomas/formz/tree/main/formz_demo)
55+//// example project.
66+17import formz/definition.{Definition}
28import formz/validation
39import formz_string/widgets
1010+import gleam/int
411import gleam/list
5121313+/// Create a basic form input. Parsed as a String.
614pub fn text_field() {
715 Definition(widgets.text_like_widget("text"), validation.string, "")
816}
9171818+/// Create an email form input. Parsed as a String but must
1919+/// look like an email address, i.e. the string has an `@`.
1020pub fn email_field() {
1121 Definition(widgets.text_like_widget("email"), validation.email, "")
1222}
13232424+/// Create a whole number form input. Parsed as an Int.
1425pub fn integer_field() {
1526 Definition(widgets.text_like_widget("number"), validation.int, 0)
1627}
17282929+/// Create a number form input. Parsed as a Float.
1830pub fn number_field() {
1931 Definition(widgets.text_like_widget("number"), validation.number, 0.0)
2032}
21333434+/// Create a checkbox form input. Parsed as a Boolean.
2235pub fn boolean_field() {
2336 Definition(widgets.checkbox_widget(), validation.boolean, False)
2437}
25383939+/// Create a password form input, which hides the input value. Parsed as a String
2640pub fn password_field() {
2741 Definition(widgets.password_widget(), validation.string, "")
2842}
29433030-pub fn enum_field(variants: List(#(String, enum))) {
3131- let assert Ok(#(_, first)) = list.first(variants)
3232- Definition(
3333- widgets.select_widget(variants),
3434- validation.enum(variants)
3535- |> validation.replace_error("Please select an option"),
3636- first,
3737- )
3838-}
4444+/// Creates a `<select>` input. Takes a tuple of #(String, String) where the first
4545+/// item in the tuple is the label, and the second item can be any Gleam type and
4646+/// is the value that would be parsed for a given selection.
4747+///
4848+/// Because of how you build `formz` forms, you need to provide a placeholder of
4949+/// the value type. Is this annoying? Would it be more or less annoying if I
5050+/// required a non-empty list for the variants instead? I'm not sure. Let me know!
5151+pub fn choices_field(
5252+ variants: List(#(String, enum)),
5353+ placeholder placeholder: enum,
5454+) {
5555+ let keys_indexed =
5656+ variants
5757+ |> list.index_map(fn(t, i) { #(t.0, int.to_string(i)) })
39584040-pub fn indexed_enum_field(variants: List(#(String, enum))) {
4141- let keys_indexed = list.index_map(variants, fn(t, i) { #(t.0, i) })
4242- let assert Ok(#(_, first)) = list.first(variants)
5959+ let values = variants |> list.map(fn(t) { t.1 })
6060+4361 Definition(
4462 widgets.select_widget(keys_indexed),
4545- validation.enum_by_index(variants)
6363+ validation.list_item_by_index(values)
4664 |> validation.replace_error("Please select an option"),
4747- first,
6565+ placeholder,
4866 )
4967}
50686969+/// Creates a `<select>` input from a list of strings. Validates that the parsed
7070+/// value is one of the strings in the list.
5171pub fn list_field(variants: List(String)) {
5252- let tuple_list = list.map(variants, fn(s) { #(s, s) })
5353- indexed_enum_field(tuple_list)
7272+ let labels_and_values = list.map(variants, fn(s) { #(s, s) })
7373+ choices_field(labels_and_values, "")
5474}