this repo has no description
4
fork

Configure Feed

Select the types of activity you want to include in your feed.

add lustre and nakai builders, and make demo site

with only 1 example so far
add hidden property to fields. not sure about this.
tweak api a little

+1427 -95
+32 -9
formz/src/formz/field.gleam
··· 2 2 import formz/validation 3 3 import gleam/option 4 4 import gleam/result 5 + import gleam/string 5 6 import justin 6 7 7 8 pub fn text_field(widget: fn(Input(format)) -> format) -> Field(format, String) { ··· 40 41 field: Field(format, output), 41 42 ) -> Field(format, output) { 42 43 Field( 43 - Input(name, justin.sentence_case(name), "", field.input.render, ""), 44 + Input(name, justin.sentence_case(name), "", field.input.render, False, ""), 45 + field.default, 46 + field.transform, 47 + ) 48 + } 49 + 50 + pub fn hidden( 51 + name: String, 52 + field: Field(format, output), 53 + ) -> Field(format, output) { 54 + Field( 55 + Input(name, "", "", field.input.render, True, ""), 44 56 field.default, 45 57 field.transform, 46 58 ) ··· 53 65 field: Field(format, output), 54 66 ) -> Field(format, output) { 55 67 Field( 56 - Input(name, label, help_text, field.input.render, ""), 68 + Input(name, label, help_text, field.input.render, False, ""), 57 69 field.default, 58 70 field.transform, 59 71 ) 60 72 } 61 73 62 - pub fn name(field: Field(format, b), name: String) -> Field(format, b) { 74 + pub fn set_name(field: Field(format, b), name: String) -> Field(format, b) { 63 75 Field(..field, input: input.set_name(field.input, name)) 64 76 } 65 77 66 - pub fn label(field: Field(format, b), label: String) -> Field(format, b) { 78 + pub fn set_label(field: Field(format, b), label: String) -> Field(format, b) { 67 79 Field(..field, input: input.set_label(field.input, label)) 68 80 } 69 81 70 - pub fn help_text(field: Field(format, b), help_text: String) -> Field(format, b) { 82 + pub fn set_help_text( 83 + field: Field(format, b), 84 + help_text: String, 85 + ) -> Field(format, b) { 71 86 Field(..field, input: input.set_help_text(field.input, help_text)) 72 87 } 73 88 74 - pub fn optional(field: Field(format, b)) -> Field(format, option.Option(b)) { 89 + pub fn set_value(field: Field(format, b), value: String) -> Field(format, b) { 90 + Field(..field, input: input.set_value(field.input, value)) 91 + } 92 + 93 + pub fn set_hidden(field: Field(format, b), hidden: Bool) -> Field(format, b) { 94 + Field(..field, input: input.set_hidden(field.input, hidden)) 95 + } 96 + 97 + pub fn set_optional(field: Field(format, b)) -> Field(format, option.Option(b)) { 75 98 Field(input: field.input, default: option.None, transform: fn(str) { 76 - case str { 99 + case string.trim(str) { 77 100 "" -> Ok(option.None) 78 101 _ -> result.map(field.transform(str), option.Some) 79 102 } 80 103 }) 81 104 } 82 105 83 - pub fn validate( 106 + pub fn validates( 84 107 field: Field(format, b), 85 108 next: fn(b) -> Result(b, String), 86 109 ) -> Field(format, b) { ··· 94 117 }) 95 118 } 96 119 97 - pub fn transform( 120 + pub fn transforms( 98 121 field: Field(format, b), 99 122 next: fn(b) -> Result(c, String), 100 123 default: c,
+1
formz/src/formz/formz_pipes.gleam
··· 3 3 // date time handling https://hexdocs.pm/birl/index.html 4 4 5 5 // TODO 6 + // - hidden fields 6 7 // - list fields 7 8 // - form sets 8 9 // - decoders/toy.decoder
+15 -13
formz/src/formz/formz_use.gleam
··· 79 79 input_data: List(#(String, String)), 80 80 ) -> Form(format, output) { 81 81 let data = dict.from_list(input_data) 82 - let Form(fields, parse) = form 83 - fields 84 - |> list.map(fn(field) { 85 - case dict.get(data, field.name) { 86 - Ok(value) -> 87 - Input( 88 - name: field.name, 89 - label: field.label, 90 - help_text: field.help_text, 91 - render: field.render, 92 - value: value, 93 - ) 94 - Error(_) -> field 82 + let Form(inputs, parse) = form 83 + inputs 84 + |> list.map(fn(input) { 85 + case dict.get(data, input.name) { 86 + Ok(value) -> input.set_value(input, value) 87 + Error(_) -> input 95 88 } 96 89 }) 97 90 |> Form(parse) ··· 120 113 121 114 pub fn get_inputs(form: Form(format, ouput)) -> List(Input(format)) { 122 115 form.inputs 116 + } 117 + 118 + pub fn get_input( 119 + form: Form(format, output), 120 + name: String, 121 + ) -> Result(Input(format), Nil) { 122 + form.inputs 123 + |> list.filter(fn(input) { input.name == name }) 124 + |> list.first 123 125 } 124 126 125 127 pub fn update_input(
+36 -25
formz/src/formz/input.gleam
··· 4 4 label: String, 5 5 help_text: String, 6 6 render: fn(Input(format)) -> format, 7 + hidden: Bool, 7 8 value: String, 8 9 ) 9 10 InvalidInput( ··· 11 12 label: String, 12 13 help_text: String, 13 14 render: fn(Input(format)) -> format, 15 + hidden: Bool, 14 16 value: String, 15 17 error: String, 16 18 ) 17 19 } 18 20 19 21 pub fn empty_field(render: fn(Input(format)) -> format) -> Input(format) { 20 - Input("", "", "", render, "") 22 + Input("", "", "", render, False, "") 21 23 } 22 24 23 25 pub fn set_name(field: Input(format), name: String) -> Input(format) { 24 26 case field { 25 - Input(_, label, help_text, render, value) -> 26 - Input(name, label, help_text, render, value) 27 - InvalidInput(_, label, help_text, render, value, error) -> 28 - InvalidInput(name, label, help_text, render, value, error) 27 + Input(_, label, help_text, render, hidden, value) -> 28 + Input(name, label, help_text, render, hidden, value) 29 + InvalidInput(_, label, help_text, render, hidden, value, error) -> 30 + InvalidInput(name, label, help_text, render, hidden, value, error) 29 31 } 30 32 } 31 33 32 34 pub fn set_label(field: Input(format), label: String) -> Input(format) { 33 35 case field { 34 - Input(name, _, help_text, render, value) -> 35 - Input(name, label, help_text, render, value) 36 - InvalidInput(name, _, help_text, render, value, error) -> 37 - InvalidInput(name, label, help_text, render, value, error) 36 + Input(name, _, help_text, render, hidden, value) -> 37 + Input(name, label, help_text, render, hidden, value) 38 + InvalidInput(name, _, help_text, render, hidden, value, error) -> 39 + InvalidInput(name, label, help_text, render, hidden, value, error) 38 40 } 39 41 } 40 42 41 43 pub fn set_help_text(field: Input(format), help_text: String) -> Input(format) { 42 44 case field { 43 - Input(name, label, _, render, value) -> 44 - Input(name, label, help_text, render, value) 45 - InvalidInput(name, label, _, render, value, error) -> 46 - InvalidInput(name, label, help_text, render, value, error) 45 + Input(name, label, _, render, hidden, value) -> 46 + Input(name, label, help_text, render, hidden, value) 47 + InvalidInput(name, label, _, render, hidden, value, error) -> 48 + InvalidInput(name, label, help_text, render, hidden, value, error) 47 49 } 48 50 } 49 51 ··· 52 54 render: fn(Input(format)) -> format, 53 55 ) -> Input(format) { 54 56 case field { 55 - Input(name, label, help_text, _, value) -> 56 - Input(name, label, help_text, render, value) 57 - InvalidInput(name, label, help_text, _, value, error) -> 58 - InvalidInput(name, label, help_text, render, value, error) 57 + Input(name, label, help_text, _, hidden, value) -> 58 + Input(name, label, help_text, render, hidden, value) 59 + InvalidInput(name, label, help_text, _, hidden, value, error) -> 60 + InvalidInput(name, label, help_text, render, hidden, value, error) 61 + } 62 + } 63 + 64 + pub fn set_hidden(field: Input(format), hidden: Bool) -> Input(format) { 65 + case field { 66 + Input(name, label, help_text, render, _, value) -> 67 + Input(name, label, help_text, render, hidden, value) 68 + InvalidInput(name, label, help_text, render, _, value, error) -> 69 + InvalidInput(name, label, help_text, render, hidden, value, error) 59 70 } 60 71 } 61 72 62 73 pub fn set_value(field: Input(format), value: String) -> Input(format) { 63 74 case field { 64 - Input(name, label, help_text, render, _) -> 65 - Input(name, label, help_text, render, value) 66 - InvalidInput(name, label, help_text, render, _, error) -> 67 - InvalidInput(name, label, help_text, render, value, error) 75 + Input(name, label, help_text, render, hidden, _) -> 76 + Input(name, label, help_text, render, hidden, value) 77 + InvalidInput(name, label, help_text, render, hidden, _, error) -> 78 + InvalidInput(name, label, help_text, render, hidden, value, error) 68 79 } 69 80 } 70 81 71 82 pub fn set_error(field: Input(format), error: String) -> Input(format) { 72 83 case field { 73 - Input(name, label, help_text, render, value) -> 74 - InvalidInput(name, label, help_text, render, value, error) 75 - InvalidInput(name, label, help_text, render, value, _) -> 76 - InvalidInput(name, label, help_text, render, value, error) 84 + Input(name, label, help_text, render, hidden, value) -> 85 + InvalidInput(name, label, help_text, render, hidden, value, error) 86 + InvalidInput(name, label, help_text, render, hidden, value, _) -> 87 + InvalidInput(name, label, help_text, render, hidden, value, error) 77 88 } 78 89 }
-48
formz/src/formz/string_fields.gleam
··· 1 - import formz/field 2 - import formz/input.{type Input} 3 - 4 - pub fn checkbox_widget(_f) -> String { 5 - "<input type=\"checkbox\">" 6 - } 7 - 8 - pub fn password_widget(_f) -> String { 9 - "<input type=\"password\">" 10 - } 11 - 12 - pub fn text_widget(f: Input(String)) -> String { 13 - let placeholder = "" 14 - 15 - "<input name=\"" 16 - <> f.name 17 - <> "\" placeholder=\"" 18 - <> placeholder 19 - <> "\" type=\"text\" value=\"" 20 - <> f.value 21 - <> "\">" 22 - } 23 - 24 - pub fn textarea_widget(_f) -> String { 25 - // https://chriscoyier.net/2023/09/29/css-solves-auto-expanding-textareas-probably-eventually/ 26 - // https://til.simonwillison.net/css/resizing-textarea 27 - "<textarea></textarea>" 28 - } 29 - 30 - pub fn text_field() { 31 - field.text_field(text_widget) 32 - } 33 - 34 - pub fn email_field() { 35 - field.email_field(text_widget) 36 - } 37 - 38 - pub fn integer_field() { 39 - field.integer_field(text_widget) 40 - } 41 - 42 - pub fn number_field() { 43 - field.number_field(text_widget) 44 - } 45 - 46 - pub fn boolean_field() { 47 - field.boolean_field(checkbox_widget) 48 - }
+22
formz/src/formz/string_generator/fields.gleam
··· 1 + import formz/field 2 + import formz/string_generator/widgets 3 + 4 + pub fn text_field() { 5 + field.text_field(widgets.text_widget) 6 + } 7 + 8 + pub fn email_field() { 9 + field.email_field(widgets.text_widget) 10 + } 11 + 12 + pub fn integer_field() { 13 + field.integer_field(widgets.text_widget) 14 + } 15 + 16 + pub fn number_field() { 17 + field.number_field(widgets.text_widget) 18 + } 19 + 20 + pub fn boolean_field() { 21 + field.boolean_field(widgets.checkbox_widget) 22 + }
+55
formz/src/formz/string_generator/simple.gleam
··· 1 + import formz/formz_use as formz 2 + import formz/input.{type Input, Input, InvalidInput} 3 + import gleam/list 4 + import gleam/string 5 + 6 + pub fn generate_form(form) -> String { 7 + { 8 + form 9 + |> formz.get_inputs 10 + |> list.filter(fn(f) { !f.hidden }) 11 + |> list.map(generate_visible_field) 12 + |> string.join("\n") 13 + } 14 + <> { 15 + form 16 + |> formz.get_inputs 17 + |> list.filter(fn(f) { f.hidden }) 18 + |> list.map(generate_hidden_field) 19 + |> string.join("\n") 20 + } 21 + } 22 + 23 + pub fn generate_visible_field(f: Input(String)) -> String { 24 + let label_el = "<label>" <> f.label <> ": </label>" 25 + let description_el = case string.is_empty(f.help_text) { 26 + True -> "" 27 + False -> "<span class=\"description\">" <> f.help_text <> "</span>" 28 + } 29 + let widget_el = "<span class=\"widget\">" <> f.render(f) <> "</span>" 30 + 31 + let errors_el = case f { 32 + Input(..) -> "<span class=\"error-placeholder\"></span>" 33 + InvalidInput(error:, ..) -> "<span class=\"errors\">" <> error <> "</span>" 34 + } 35 + 36 + "<p class=\"simple_field\">" 37 + <> label_el 38 + <> description_el 39 + <> widget_el 40 + <> errors_el 41 + <> "</p>" 42 + } 43 + 44 + pub fn generate_hidden_field(f: Input(String)) -> String { 45 + case f.hidden { 46 + False -> "" 47 + True -> { 48 + "<input type=\"hidden\" name=\"" 49 + <> f.name 50 + <> "\" value=\"" 51 + <> f.value 52 + <> "\" />" 53 + } 54 + } 55 + }
formz/src/formz/string_generator/table.gleam

This is a binary file and will not be displayed.

+29
formz/src/formz/string_generator/widgets.gleam
··· 1 + import formz/input.{type Input} 2 + 3 + pub fn checkbox_widget(f: Input(String)) -> String { 4 + "<input type=\"checkbox\">" 5 + } 6 + 7 + pub fn password_widget(f: Input(String)) -> String { 8 + "<input type=\"password\">" 9 + } 10 + 11 + pub fn text_widget(f: Input(String)) -> String { 12 + let aria_label = case f.label { 13 + "" -> "" 14 + _ -> " aria-label=\"" <> f.label <> "\"" 15 + } 16 + 17 + "<input " 18 + <> { " name=\"" <> f.name <> "\"" } 19 + <> { " type=\"text\"" } 20 + <> { " value=\"" <> f.value <> "\"" } 21 + <> { aria_label } 22 + <> ">" 23 + } 24 + 25 + pub fn textarea_widget(f: Input(String)) -> String { 26 + // https://chriscoyier.net/2023/09/29/css-solves-auto-expanding-textareas-probably-eventually/ 27 + // https://til.simonwillison.net/css/resizing-textarea 28 + "<textarea></textarea>" 29 + }
+23
formz_demo/.github/workflows/test.yml
··· 1 + name: test 2 + 3 + on: 4 + push: 5 + branches: 6 + - master 7 + - main 8 + pull_request: 9 + 10 + jobs: 11 + test: 12 + runs-on: ubuntu-latest 13 + steps: 14 + - uses: actions/checkout@v4 15 + - uses: erlef/setup-beam@v1 16 + with: 17 + otp-version: "26.0.2" 18 + gleam-version: "1.5.0" 19 + rebar3-version: "3" 20 + # elixir-version: "1.15.4" 21 + - run: gleam deps download 22 + - run: gleam test 23 + - run: gleam format --check src test
+4
formz_demo/.gitignore
··· 1 + *.beam 2 + *.ez 3 + /build 4 + erl_crash.dump
+24
formz_demo/README.md
··· 1 + # formz_demo 2 + 3 + [![Package Version](https://img.shields.io/hexpm/v/formz_demo)](https://hex.pm/packages/formz_demo) 4 + [![Hex Docs](https://img.shields.io/badge/hex-docs-ffaff3)](https://hexdocs.pm/formz_demo/) 5 + 6 + ```sh 7 + gleam add formz_demo@1 8 + ``` 9 + ```gleam 10 + import formz_demo 11 + 12 + pub fn main() { 13 + // TODO: An example of the project in use 14 + } 15 + ``` 16 + 17 + Further documentation can be found at <https://hexdocs.pm/formz_demo>. 18 + 19 + ## Development 20 + 21 + ```sh 22 + gleam run # Run the project 23 + gleam test # Run the tests 24 + ```
+28
formz_demo/gleam.toml
··· 1 + name = "formz_demo" 2 + version = "1.0.0" 3 + 4 + # Fill out these fields if you intend to generate HTML documentation or publish 5 + # your project to the Hex package manager. 6 + # 7 + # description = "" 8 + # licences = ["Apache-2.0"] 9 + # repository = { type = "github", user = "", repo = "" } 10 + # links = [{ title = "Website", href = "" }] 11 + # 12 + # For a full reference of all the available options, you can have a look at 13 + # https://gleam.run/writing-gleam/gleam-toml/. 14 + 15 + [dependencies] 16 + formz = { path = "../formz" } 17 + formz_lustre = { path = "../formz_lustre" } 18 + formz_nakai = { path = "../formz_nakai" } 19 + gleam_http = ">= 3.7.0 and < 4.0.0" 20 + gleam_erlang = ">= 0.27.0 and < 1.0.0" 21 + gleam_stdlib = ">= 0.34.0 and < 2.0.0" 22 + mist = ">= 3.0.0 and < 4.0.0" 23 + wisp = ">= 1.2.0 and < 2.0.0" 24 + lustre = ">= 4.5.1 and < 5.0.0" 25 + nakai = ">= 1.0.0 and < 2.0.0" 26 + 27 + [dev-dependencies] 28 + gleeunit = ">= 1.0.0 and < 2.0.0"
+48
formz_demo/manifest.toml
··· 1 + # This file was generated by Gleam 2 + # You typically do not need to edit this file 3 + 4 + packages = [ 5 + { name = "birl", version = "1.7.1", build_tools = ["gleam"], requirements = ["gleam_stdlib", "ranger"], otp_app = "birl", source = "hex", outer_checksum = "5C66647D62BCB11FE327E7A6024907C4A17954EF22865FE0940B54A852446D01" }, 6 + { name = "directories", version = "1.1.0", build_tools = ["gleam"], requirements = ["envoy", "gleam_stdlib", "platform", "simplifile"], otp_app = "directories", source = "hex", outer_checksum = "BDA521A4EB9EE3A7894F0DC863797878E91FF5C7826F7084B2E731E208BDB076" }, 7 + { name = "envoy", version = "1.0.1", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "envoy", source = "hex", outer_checksum = "CFAACCCFC47654F7E8B75E614746ED924C65BD08B1DE21101548AC314A8B6A41" }, 8 + { name = "exception", version = "2.0.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "exception", source = "hex", outer_checksum = "F5580D584F16A20B7FCDCABF9E9BE9A2C1F6AC4F9176FA6DD0B63E3B20D450AA" }, 9 + { name = "filepath", version = "1.0.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "filepath", source = "hex", outer_checksum = "EFB6FF65C98B2A16378ABC3EE2B14124168C0CE5201553DE652E2644DCFDB594" }, 10 + { name = "formz", version = "1.0.0", build_tools = ["gleam"], requirements = ["gleam_stdlib", "justin"], source = "local", path = "../formz" }, 11 + { name = "formz_lustre", version = "1.0.0", build_tools = ["gleam"], requirements = ["formz", "gleam_stdlib", "lustre"], source = "local", path = "../formz_lustre" }, 12 + { name = "formz_nakai", version = "1.0.0", build_tools = ["gleam"], requirements = ["formz", "gleam_stdlib", "nakai"], source = "local", path = "../formz_nakai" }, 13 + { name = "gleam_crypto", version = "1.4.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleam_crypto", source = "hex", outer_checksum = "8AE56026B3E05EBB1F076778478A762E9EB62B31AEEB4285755452F397029D22" }, 14 + { name = "gleam_erlang", version = "0.27.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleam_erlang", source = "hex", outer_checksum = "DE468F676D71B313C6C8C5334425CFCF827837333F8AB47B64D8A6D7AA40185D" }, 15 + { name = "gleam_http", version = "3.7.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleam_http", source = "hex", outer_checksum = "EA66440C2269F7CED0F6845E5BD0DB68095775D627FA709A841CA78A398D6D56" }, 16 + { name = "gleam_json", version = "1.0.1", build_tools = ["gleam"], requirements = ["gleam_stdlib", "thoas"], otp_app = "gleam_json", source = "hex", outer_checksum = "9063D14D25406326C0255BDA0021541E797D8A7A12573D849462CAFED459F6EB" }, 17 + { name = "gleam_otp", version = "0.12.1", build_tools = ["gleam"], requirements = ["gleam_erlang", "gleam_stdlib"], otp_app = "gleam_otp", source = "hex", outer_checksum = "BFACC1513410DF5A1617169A9CD7EA334973AC71D860A17574BA7B2EADD89A6F" }, 18 + { name = "gleam_stdlib", version = "0.40.0", build_tools = ["gleam"], requirements = [], otp_app = "gleam_stdlib", source = "hex", outer_checksum = "86606B75A600BBD05E539EB59FABC6E307EEEA7B1E5865AFB6D980A93BCB2181" }, 19 + { name = "gleeunit", version = "1.2.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleeunit", source = "hex", outer_checksum = "F7A7228925D3EE7D0813C922E062BFD6D7E9310F0BEE585D3A42F3307E3CFD13" }, 20 + { name = "glisten", version = "6.0.0", build_tools = ["gleam"], requirements = ["gleam_erlang", "gleam_otp", "gleam_stdlib", "logging", "telemetry"], otp_app = "glisten", source = "hex", outer_checksum = "912132751031473CB38F454120124FFC96AF6B0EA33D92C9C90DB16327A2A972" }, 21 + { name = "gramps", version = "2.0.3", build_tools = ["gleam"], requirements = ["gleam_crypto", "gleam_erlang", "gleam_http", "gleam_stdlib"], otp_app = "gramps", source = "hex", outer_checksum = "3CCAA6E081225180D95C79679D383BBF51C8D1FDC1B84DA1DA444F628C373793" }, 22 + { name = "hpack_erl", version = "0.3.0", build_tools = ["rebar3"], requirements = [], otp_app = "hpack", source = "hex", outer_checksum = "D6137D7079169D8C485C6962DFE261AF5B9EF60FBC557344511C1E65E3D95FB0" }, 23 + { name = "justin", version = "1.0.1", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "justin", source = "hex", outer_checksum = "7FA0C6DB78640C6DC5FBFD59BF3456009F3F8B485BF6825E97E1EB44E9A1E2CD" }, 24 + { name = "logging", version = "1.3.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "logging", source = "hex", outer_checksum = "1098FBF10B54B44C2C7FDF0B01C1253CAFACDACABEFB4B0D027803246753E06D" }, 25 + { name = "lustre", version = "4.5.1", build_tools = ["gleam"], requirements = ["gleam_erlang", "gleam_json", "gleam_otp", "gleam_stdlib"], otp_app = "lustre", source = "hex", outer_checksum = "B592DA442F6577143CAFA35D4506DB2018DAEED9C707A921E33559E09F001DF1" }, 26 + { name = "marceau", version = "1.2.0", build_tools = ["gleam"], requirements = [], otp_app = "marceau", source = "hex", outer_checksum = "5188D643C181EE350D8A20A3BDBD63AF7B6C505DE333CFBE05EF642ADD88A59B" }, 27 + { name = "mist", version = "3.0.0", build_tools = ["gleam"], requirements = ["birl", "gleam_erlang", "gleam_http", "gleam_otp", "gleam_stdlib", "glisten", "gramps", "hpack_erl", "logging"], otp_app = "mist", source = "hex", outer_checksum = "CDA1A74E768419235E16886463EC4722EFF4AB3F8D820A76EAD45D7C167D7282" }, 28 + { name = "nakai", version = "1.0.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "nakai", source = "hex", outer_checksum = "FD55B7926640FFD21FCC048D7E21752ED94FD424E1A731DC5C9DFB9928007F08" }, 29 + { name = "platform", version = "1.0.0", build_tools = ["gleam"], requirements = [], otp_app = "platform", source = "hex", outer_checksum = "8339420A95AD89AAC0F82F4C3DB8DD401041742D6C3F46132A8739F6AEB75391" }, 30 + { name = "ranger", version = "1.2.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "ranger", source = "hex", outer_checksum = "1566C272B1D141B3BBA38B25CB761EF56E312E79EC0E2DFD4D3C19FB0CC1F98C" }, 31 + { name = "simplifile", version = "2.2.0", build_tools = ["gleam"], requirements = ["filepath", "gleam_stdlib"], otp_app = "simplifile", source = "hex", outer_checksum = "0DFABEF7DC7A9E2FF4BB27B108034E60C81BEBFCB7AB816B9E7E18ED4503ACD8" }, 32 + { name = "telemetry", version = "1.3.0", build_tools = ["rebar3"], requirements = [], otp_app = "telemetry", source = "hex", outer_checksum = "7015FC8919DBE63764F4B4B87A95B7C0996BD539E0D499BE6EC9D7F3875B79E6" }, 33 + { name = "thoas", version = "1.2.1", build_tools = ["rebar3"], requirements = [], otp_app = "thoas", source = "hex", outer_checksum = "E38697EDFFD6E91BD12CEA41B155115282630075C2A727E7A6B2947F5408B86A" }, 34 + { name = "wisp", version = "1.2.0", build_tools = ["gleam"], requirements = ["directories", "exception", "gleam_crypto", "gleam_erlang", "gleam_http", "gleam_json", "gleam_stdlib", "logging", "marceau", "mist", "simplifile"], otp_app = "wisp", source = "hex", outer_checksum = "F71265D2F1DE11426535A2FA1DA3B11D2FFB783B116DF9496BC8C41983EBADB4" }, 35 + ] 36 + 37 + [requirements] 38 + formz = { path = "../formz" } 39 + formz_lustre = { path = "../formz_lustre" } 40 + formz_nakai = { path = "../formz_nakai" } 41 + gleam_erlang = { version = ">= 0.27.0 and < 1.0.0" } 42 + gleam_http = { version = ">= 3.7.0 and < 4.0.0" } 43 + gleam_stdlib = { version = ">= 0.34.0 and < 2.0.0" } 44 + gleeunit = { version = ">= 1.0.0 and < 2.0.0" } 45 + lustre = { version = ">= 4.5.1 and < 5.0.0" } 46 + mist = { version = ">= 3.0.0 and < 4.0.0" } 47 + nakai = { version = ">= 1.0.0 and < 2.0.0" } 48 + wisp = { version = ">= 1.2.0 and < 2.0.0" }
+109
formz_demo/priv/static/highlight-gleam.mjs
··· 1 + function gleam(hljs) { 2 + const KEYWORDS = { 3 + className: "keyword", 4 + beginKeywords: 5 + "as assert auto case const delegate derive echo else fn if " + 6 + "implement import let macro opaque panic pub test todo type use", 7 + }; 8 + const STRING = { 9 + className: "string", 10 + variants: [{ begin: /"/, end: /"/ }], 11 + contains: [hljs.BACKSLASH_ESCAPE], 12 + relevance: 0, 13 + }; 14 + const NAME = { 15 + className: "variable", 16 + begin: "\\b[a-z][a-z0-9_]*\\b", 17 + relevance: 0, 18 + }; 19 + const DISCARD_NAME = { 20 + className: "comment", 21 + begin: "\\b_[a-z][a-z0-9_]*\\b", 22 + relevance: 0, 23 + }; 24 + const NUMBER = { 25 + className: "number", 26 + variants: [ 27 + { 28 + // binary 29 + begin: "\\b0[bB](?:_?[01]+)+", 30 + }, 31 + { 32 + // octal 33 + begin: "\\b0[oO](?:_?[0-7]+)+", 34 + }, 35 + { 36 + // hex 37 + begin: "\\b0[xX](?:_?[0-9a-fA-F]+)+", 38 + }, 39 + { 40 + // dec, float 41 + begin: "\\b\\d(?:_?\\d+)*(?:\\.(?:\\d(?:_?\\d+)*)*)?", 42 + }, 43 + ], 44 + relevance: 0, 45 + }; 46 + 47 + return { 48 + name: "Gleam", 49 + aliases: ["gleam"], 50 + contains: [ 51 + hljs.C_LINE_COMMENT_MODE, 52 + STRING, 53 + { 54 + // bit array 55 + begin: "<<", 56 + end: ">>", 57 + contains: [ 58 + { 59 + className: "keyword", 60 + beginKeywords: 61 + "binary bits bytes int float bit_string bit_array bits utf8 utf16 " + 62 + "utf32 utf8_codepoint utf16_codepoint utf32_codepoint signed " + 63 + "unsigned big little native unit size", 64 + }, 65 + KEYWORDS, 66 + STRING, 67 + NAME, 68 + DISCARD_NAME, 69 + NUMBER, 70 + ], 71 + relevance: 10, 72 + }, 73 + { 74 + className: "function", 75 + beginKeywords: "fn", 76 + end: "\\(", 77 + excludeEnd: true, 78 + contains: [ 79 + { 80 + className: "title", 81 + begin: "[a-z][a-z0-9_]*\\w*", 82 + relevance: 0, 83 + }, 84 + ], 85 + }, 86 + { 87 + className: "attribute", 88 + begin: "@", 89 + end: "\\(", 90 + excludeEnd: true, 91 + }, 92 + KEYWORDS, 93 + { 94 + // Type names and constructors 95 + className: "title", 96 + begin: "\\b[A-Z][A-Za-z0-9]*\\b", 97 + relevance: 0, 98 + }, 99 + { 100 + className: "operator", 101 + begin: "[+\\-*/%!=<>&|.]+", 102 + relevance: 0, 103 + }, 104 + NAME, 105 + DISCARD_NAME, 106 + NUMBER, 107 + ], 108 + }; 109 + }
+115
formz_demo/src/app/example.gleam
··· 1 + import formz/field 2 + import formz/formz_use as formz 3 + import formz/input 4 + import formz/string_generator/fields as string_fields 5 + import formz_demo/page 6 + import formz_lustre/fields as lustre_fields 7 + import formz_nakai/fields as nakai_fields 8 + import gleam/http.{Get, Post} 9 + import gleam/list 10 + import gleam/option 11 + import gleam/result 12 + import wisp.{type Request, type Response} 13 + 14 + pub fn handle(req: Request, make_forms) -> Response { 15 + case req.method { 16 + Get -> handle_get(make_forms) 17 + Post -> handle_post(req, make_forms) 18 + _ -> wisp.method_not_allowed(allowed: [Get, Post]) 19 + } 20 + } 21 + 22 + fn handle_get(make_forms) { 23 + let #(code, string_form, lustre_form, nakai_form) = make_forms() 24 + page.build_page( 25 + "Hello World", 26 + code, 27 + [], 28 + [], 29 + option.None, 30 + string_form, 31 + lustre_form, 32 + nakai_form, 33 + ) 34 + } 35 + 36 + fn handle_post(req: Request, make_forms) -> Response { 37 + use formdata <- wisp.require_form(req) 38 + 39 + let forms = make_forms() 40 + let #(code, string_form, lustre_form, nakai_form) = forms 41 + 42 + let generator = 43 + result.unwrap(list.key_find(formdata.values, "__generator"), "string") 44 + 45 + let input_data = 46 + formdata.values |> list.filter(fn(t) { t.0 != "__generator" }) 47 + 48 + case generator { 49 + "lustre" -> { 50 + let #(lustre_form, output) = case 51 + lustre_form |> formz.data(input_data) |> formz.parse 52 + { 53 + Ok(r) -> #(lustre_form, option.Some(r)) 54 + Error(f) -> #(f, option.None) 55 + } 56 + page.build_page( 57 + "Hello World", 58 + code, 59 + input_data, 60 + get_errors(lustre_form), 61 + output, 62 + string_form, 63 + lustre_form, 64 + nakai_form, 65 + ) 66 + } 67 + "nakai" -> { 68 + let #(nakai_form, output) = case 69 + nakai_form |> formz.data(input_data) |> formz.parse 70 + { 71 + Ok(r) -> #(nakai_form, option.Some(r)) 72 + Error(f) -> #(f, option.None) 73 + } 74 + page.build_page( 75 + "Hello World", 76 + code, 77 + input_data, 78 + get_errors(nakai_form), 79 + output, 80 + string_form, 81 + lustre_form, 82 + nakai_form, 83 + ) 84 + } 85 + _ -> { 86 + let #(string_form, output) = case 87 + string_form |> formz.data(input_data) |> formz.parse 88 + { 89 + Ok(r) -> #(string_form, option.Some(r)) 90 + Error(f) -> #(f, option.None) 91 + } 92 + page.build_page( 93 + "Hello World", 94 + code, 95 + input_data, 96 + get_errors(string_form), 97 + output, 98 + string_form, 99 + lustre_form, 100 + nakai_form, 101 + ) 102 + } 103 + } 104 + } 105 + 106 + fn get_errors(form: formz.Form(format, a)) -> List(#(String, String)) { 107 + form 108 + |> formz.get_inputs 109 + |> list.filter_map(fn(f) { 110 + case f { 111 + input.Input(..) -> Error(Nil) 112 + input.InvalidInput(error:, ..) -> Ok(#(f.name, error)) 113 + } 114 + }) 115 + }
+30
formz_demo/src/app/examples/example_1.gleam
··· 1 + import formz/field 2 + import formz/formz_use as formz 3 + import formz/string_generator/fields as string_fields 4 + import formz_lustre/fields as lustre_fields 5 + import formz_nakai/fields as nakai_fields 6 + 7 + pub fn make_forms() { 8 + #( 9 + "import formz 10 + import formz/field.{field} 11 + import formz/string_generator/fields 12 + 13 + pub fn hello_world_form() { 14 + use name <- formz.with(field(\"name\", fields.text_field())) 15 + formz.create_form(\"Hello \" <> name) 16 + }", 17 + { 18 + use name <- formz.with(field.field("name", string_fields.text_field())) 19 + formz.create_form("Hello " <> name) 20 + }, 21 + { 22 + use name <- formz.with(field.field("name", lustre_fields.text_field())) 23 + formz.create_form("Hello " <> name) 24 + }, 25 + { 26 + use name <- formz.with(field.field("name", nakai_fields.text_field())) 27 + formz.create_form("Hello " <> name) 28 + }, 29 + ) 30 + }
+38
formz_demo/src/app/router.gleam
··· 1 + import app/example 2 + import gleam/http/request 3 + import lustre/attribute 4 + import lustre/element 5 + import lustre/element/html.{html} 6 + import wisp.{type Request, type Response} 7 + 8 + import app/examples/example_1 9 + 10 + import app/web.{type Context} 11 + 12 + pub fn handle_request(req: Request, ctx: Context) -> Response { 13 + use req <- web.middleware(req, ctx) 14 + 15 + case request.path_segments(req) { 16 + [] -> index() 17 + ["example-1"] -> example.handle(req, example_1.make_forms) 18 + _ -> wisp.not_found() 19 + } 20 + } 21 + 22 + pub fn index() -> Response { 23 + let html = 24 + html([], [ 25 + html.head([], [html.title([], "Gleam formz!")]), 26 + html.body([], [ 27 + html.h1([], [html.text("Hey there, formz!")]), 28 + html.ul([], [ 29 + html.li([], [ 30 + html.a([attribute.href("/example-1")], [html.text("Example 1")]), 31 + ]), 32 + ]), 33 + ]), 34 + ]) 35 + |> element.to_document_string_builder 36 + 37 + wisp.ok() |> wisp.html_body(html) 38 + }
+19
formz_demo/src/app/web.gleam
··· 1 + import wisp 2 + 3 + pub type Context { 4 + Context(static_directory: String) 5 + } 6 + 7 + pub fn middleware( 8 + req: wisp.Request, 9 + ctx: Context, 10 + handle_request: fn(wisp.Request) -> wisp.Response, 11 + ) -> wisp.Response { 12 + let req = wisp.method_override(req) 13 + use <- wisp.log_request(req) 14 + use <- wisp.rescue_crashes 15 + use req <- wisp.handle_head(req) 16 + use <- wisp.serve_static(req, under: "/static", from: ctx.static_directory) 17 + 18 + handle_request(req) 19 + }
+33
formz_demo/src/formz_demo.gleam
··· 1 + import app/router 2 + import app/web.{Context} 3 + import gleam/erlang/process 4 + import mist 5 + import wisp 6 + import wisp/wisp_mist 7 + 8 + pub fn main() { 9 + wisp.configure_logger() 10 + let secret_key_base = wisp.random_string(64) 11 + 12 + // A context is constructed holding the static directory path. 13 + let ctx = Context(static_directory: static_directory()) 14 + 15 + let handler = router.handle_request(_, ctx) 16 + 17 + let assert Ok(_) = 18 + wisp_mist.handler(handler, secret_key_base) 19 + |> mist.new 20 + |> mist.port(8000) 21 + |> mist.start_http 22 + 23 + process.sleep_forever() 24 + } 25 + 26 + pub fn static_directory() -> String { 27 + // The priv directory is where we store non-Gleam and non-Erlang files, 28 + // including static assets to be served. 29 + // This function returns an absolute path and works both in development and in 30 + // production after compilation. 31 + let assert Ok(priv_directory) = wisp.priv_directory("formz_demo") 32 + priv_directory <> "/static" 33 + }
+288
formz_demo/src/formz_demo/page.gleam
··· 1 + import formz/formz_use as formz 2 + import formz/string_generator/simple as simple_string 3 + import formz_lustre/simple as simple_lustre 4 + import formz_nakai/simple as simple_nakai 5 + import gleam/list 6 + import gleam/option 7 + import gleam/result 8 + import gleam/string 9 + import lustre/attribute 10 + import lustre/element 11 + import lustre/element/html.{html} 12 + import nakai 13 + import nakai/html as nhtml 14 + import wisp.{type Response} 15 + 16 + pub fn build_page( 17 + name: String, 18 + code: String, 19 + input_data: List(#(String, String)), 20 + errors_list: List(#(String, String)), 21 + output: option.Option(a), 22 + string_form: formz.Form(String, a), 23 + lustre_form: formz.Form(element.Element(msg), a), 24 + nakai_form: formz.Form(nhtml.Node, a), 25 + ) -> Response { 26 + let html = 27 + html([], [ 28 + html.head([], [ 29 + html.title([], "Gleam formz!"), 30 + html.link([ 31 + attribute.rel("stylesheet"), 32 + attribute.href( 33 + "https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/styles/default.min.css", 34 + ), 35 + ]), 36 + html.script( 37 + [ 38 + attribute.src( 39 + "https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/highlight.min.js", 40 + ), 41 + ], 42 + "", 43 + ), 44 + html.script([attribute.src("/static/highlight-gleam.mjs")], ""), 45 + html.script( 46 + [], 47 + " 48 + hljs.registerLanguage('gleam', gleam); 49 + hljs.highlightAll(); 50 + ", 51 + ), 52 + html.style( 53 + [], 54 + " 55 + body { font-family: sans-serif; } 56 + 57 + h1 { 58 + margin: 10px; 59 + font-size: 1.5em; 60 + } 61 + 62 + .container { 63 + display: grid; 64 + grid-template-columns: auto minmax(300px, 600px); 65 + grid-template-rows: auto; 66 + grid-template-areas: 67 + \"code post\" 68 + \"forms forms\"; 69 + width: 100%; 70 + } 71 + 72 + .code { 73 + grid-area: code; 74 + float; 75 + white-space: pre; 76 + margin: 0 7px 20px; 77 + 78 + code { 79 + font-family: ui-monospace, 'Cascadia Code', 'Source Code Pro', Menlo, Consolas, 'DejaVu Sans Mono', monospace; 80 + font-weight: normal; 81 + font-size: 1.1em; 82 + } 83 + } 84 + 85 + .post { 86 + > div { 87 + margin: 0 7px 20px; 88 + grid-area: post; 89 + 90 + > div { 91 + font-family: ui-monospace, 'Cascadia Code', 'Source Code Pro', Menlo, Consolas, 'DejaVu Sans Mono', monospace; 92 + min-height: 18px; 93 + } 94 + 95 + > div.success { 96 + padding: 10px; 97 + background: #90EE90; 98 + } 99 + 100 + > div.error { 101 + padding: 10px; 102 + background: #FF5733; 103 + } 104 + 105 + h2 { 106 + background: #444; 107 + color: #fafafa; 108 + padding: 5px; 109 + margin: 0; 110 + font-size: 12px; 111 + text-transform: uppercase; 112 + font-weight: normal; 113 + } 114 + } 115 + } 116 + 117 + table.input_data { 118 + border-spacing: 0; 119 + border-collapse: collapse; 120 + width: 100%; 121 + 122 + th { 123 + text-align: left; 124 + font-weight: normal; 125 + background: #bbb; 126 + padding: 10px; 127 + } 128 + 129 + td:nth-child(1), td:nth-child(2) { 130 + font-family: ui-monospace, 'Cascadia Code', 'Source Code Pro', Menlo, Consolas, 'DejaVu Sans Mono', monospace; 131 + } 132 + td { 133 + background: #eee; 134 + padding: 10px; 135 + } 136 + } 137 + 138 + table.forms { 139 + width: 100%; 140 + border-spacing: 7px 0; 141 + grid-area: forms; 142 + 143 + th { 144 + text-align: left; 145 + font-weight: normal; 146 + background: #bbb; 147 + padding: 10px; 148 + } 149 + 150 + td { 151 + background: #eee; 152 + padding: 10px; 153 + } 154 + } 155 + ", 156 + ), 157 + ]), 158 + html.body([], [ 159 + html.h1([], [html.text(name)]), 160 + html.div([attribute.class("container")], [ 161 + html.pre([attribute.class("code")], [ 162 + html.code([attribute.class("langauge-gleam")], [ 163 + html.text(code |> string.trim), 164 + ]), 165 + ]), 166 + show_post(input_data, errors_list, output), 167 + show_forms(string_form, lustre_form, nakai_form), 168 + ]), 169 + ]), 170 + ]) 171 + |> element.to_document_string_builder 172 + 173 + wisp.ok() |> wisp.html_body(html) 174 + } 175 + 176 + pub fn show_post( 177 + input_data: List(#(String, String)), 178 + errors: List(#(String, String)), 179 + output: option.Option(a), 180 + ) { 181 + let input_rows = 182 + element.fragment( 183 + list.map(input_data, fn(t) { 184 + let #(key, value) = t 185 + let error = list.key_find(errors, key) |> result.unwrap("") 186 + html.tr([], [ 187 + html.td([], [html.text(key)]), 188 + html.td([], [html.text(string.inspect(value))]), 189 + html.td([], [html.text(error)]), 190 + ]) 191 + }), 192 + ) 193 + 194 + let output_row = case output { 195 + option.None -> "" 196 + option.Some(val) -> string.inspect(val) 197 + } 198 + 199 + case input_data { 200 + [] -> element.none() 201 + _ -> 202 + html.div([attribute.class("post")], [ 203 + html.div([], [ 204 + html.h2([], [html.text("Post data")]), 205 + html.table([attribute.class("input_data")], [ 206 + html.tr([], [ 207 + html.th([], [html.text("Key")]), 208 + html.th([], [html.text("Value")]), 209 + html.th([], [html.text("Error")]), 210 + ]), 211 + input_rows, 212 + ]), 213 + html.h2([], [html.text("Output")]), 214 + html.div( 215 + [ 216 + attribute.classes([ 217 + #("success", option.is_some(output)), 218 + #("error", option.is_none(output)), 219 + ]), 220 + ], 221 + [html.text(output_row)], 222 + ), 223 + ]), 224 + ]) 225 + } 226 + } 227 + 228 + fn show_forms( 229 + string_form: formz.Form(String, j), 230 + lustre_form: formz.Form(element.Element(msg), j), 231 + nakai_form: formz.Form(nhtml.Node, j), 232 + ) -> element.Element(msg) { 233 + html.table([attribute.classes([#("forms", True)])], [ 234 + html.tr([], [ 235 + html.th([attribute.style([#("width", "33.3%")])], [html.text("String")]), 236 + html.th([attribute.style([#("width", "33.3%")])], [html.text("Lustre")]), 237 + html.th([attribute.style([#("width", "33.3%")])], [html.text("Nakai")]), 238 + ]), 239 + html.tr([], [ 240 + html.td([], [ 241 + html.form([attribute.method("POST")], [ 242 + html.div( 243 + [ 244 + attribute.attribute( 245 + "dangerous-unescaped-html", 246 + simple_string.generate_form(string_form), 247 + ), 248 + ], 249 + [], 250 + ), 251 + html.input([ 252 + attribute.type_("hidden"), 253 + attribute.value("string"), 254 + attribute.name("__generator"), 255 + ]), 256 + ]), 257 + ]), 258 + html.td([], [ 259 + html.form([attribute.method("POST")], [ 260 + simple_lustre.generate_form(lustre_form), 261 + html.input([ 262 + attribute.type_("hidden"), 263 + attribute.value("lustre"), 264 + attribute.name("__generator"), 265 + ]), 266 + ]), 267 + ]), 268 + html.td([], [ 269 + html.form([attribute.method("POST")], [ 270 + html.div( 271 + [ 272 + attribute.attribute( 273 + "dangerous-unescaped-html", 274 + simple_nakai.generate_form(nakai_form) |> nakai.to_string, 275 + ), 276 + ], 277 + [], 278 + ), 279 + html.input([ 280 + attribute.type_("hidden"), 281 + attribute.value("nakai"), 282 + attribute.name("__generator"), 283 + ]), 284 + ]), 285 + ]), 286 + ]), 287 + ]) 288 + }
+12
formz_demo/test/formz_demo_test.gleam
··· 1 + import gleeunit 2 + import gleeunit/should 3 + 4 + pub fn main() { 5 + gleeunit.main() 6 + } 7 + 8 + // gleeunit test functions end in `_test` 9 + pub fn hello_world_test() { 10 + 1 11 + |> should.equal(1) 12 + }
+23
formz_lustre/.github/workflows/test.yml
··· 1 + name: test 2 + 3 + on: 4 + push: 5 + branches: 6 + - master 7 + - main 8 + pull_request: 9 + 10 + jobs: 11 + test: 12 + runs-on: ubuntu-latest 13 + steps: 14 + - uses: actions/checkout@v4 15 + - uses: erlef/setup-beam@v1 16 + with: 17 + otp-version: "26.0.2" 18 + gleam-version: "1.5.0" 19 + rebar3-version: "3" 20 + # elixir-version: "1.15.4" 21 + - run: gleam deps download 22 + - run: gleam test 23 + - run: gleam format --check src test
+4
formz_lustre/.gitignore
··· 1 + *.beam 2 + *.ez 3 + /build 4 + erl_crash.dump
+24
formz_lustre/README.md
··· 1 + # formz_lustre 2 + 3 + [![Package Version](https://img.shields.io/hexpm/v/formz_lustre)](https://hex.pm/packages/formz_lustre) 4 + [![Hex Docs](https://img.shields.io/badge/hex-docs-ffaff3)](https://hexdocs.pm/formz_lustre/) 5 + 6 + ```sh 7 + gleam add formz_lustre@1 8 + ``` 9 + ```gleam 10 + import formz_lustre 11 + 12 + pub fn main() { 13 + // TODO: An example of the project in use 14 + } 15 + ``` 16 + 17 + Further documentation can be found at <https://hexdocs.pm/formz_lustre>. 18 + 19 + ## Development 20 + 21 + ```sh 22 + gleam run # Run the project 23 + gleam test # Run the tests 24 + ```
+21
formz_lustre/gleam.toml
··· 1 + name = "formz_lustre" 2 + version = "1.0.0" 3 + 4 + # Fill out these fields if you intend to generate HTML documentation or publish 5 + # your project to the Hex package manager. 6 + # 7 + # description = "" 8 + # licences = ["Apache-2.0"] 9 + # repository = { type = "github", user = "", repo = "" } 10 + # links = [{ title = "Website", href = "" }] 11 + # 12 + # For a full reference of all the available options, you can have a look at 13 + # https://gleam.run/writing-gleam/gleam-toml/. 14 + 15 + [dependencies] 16 + formz = { path = "../formz" } 17 + gleam_stdlib = ">= 0.34.0 and < 2.0.0" 18 + lustre = ">= 4.5.1 and < 5.0.0" 19 + 20 + [dev-dependencies] 21 + gleeunit = ">= 1.0.0 and < 2.0.0"
+20
formz_lustre/manifest.toml
··· 1 + # This file was generated by Gleam 2 + # You typically do not need to edit this file 3 + 4 + packages = [ 5 + { name = "formz", version = "1.0.0", build_tools = ["gleam"], requirements = ["gleam_stdlib", "justin"], source = "local", path = "../formz" }, 6 + { name = "gleam_erlang", version = "0.27.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleam_erlang", source = "hex", outer_checksum = "DE468F676D71B313C6C8C5334425CFCF827837333F8AB47B64D8A6D7AA40185D" }, 7 + { name = "gleam_json", version = "1.0.1", build_tools = ["gleam"], requirements = ["gleam_stdlib", "thoas"], otp_app = "gleam_json", source = "hex", outer_checksum = "9063D14D25406326C0255BDA0021541E797D8A7A12573D849462CAFED459F6EB" }, 8 + { name = "gleam_otp", version = "0.12.1", build_tools = ["gleam"], requirements = ["gleam_erlang", "gleam_stdlib"], otp_app = "gleam_otp", source = "hex", outer_checksum = "BFACC1513410DF5A1617169A9CD7EA334973AC71D860A17574BA7B2EADD89A6F" }, 9 + { name = "gleam_stdlib", version = "0.40.0", build_tools = ["gleam"], requirements = [], otp_app = "gleam_stdlib", source = "hex", outer_checksum = "86606B75A600BBD05E539EB59FABC6E307EEEA7B1E5865AFB6D980A93BCB2181" }, 10 + { name = "gleeunit", version = "1.2.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleeunit", source = "hex", outer_checksum = "F7A7228925D3EE7D0813C922E062BFD6D7E9310F0BEE585D3A42F3307E3CFD13" }, 11 + { name = "justin", version = "1.0.1", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "justin", source = "hex", outer_checksum = "7FA0C6DB78640C6DC5FBFD59BF3456009F3F8B485BF6825E97E1EB44E9A1E2CD" }, 12 + { name = "lustre", version = "4.5.1", build_tools = ["gleam"], requirements = ["gleam_erlang", "gleam_json", "gleam_otp", "gleam_stdlib"], otp_app = "lustre", source = "hex", outer_checksum = "B592DA442F6577143CAFA35D4506DB2018DAEED9C707A921E33559E09F001DF1" }, 13 + { name = "thoas", version = "1.2.1", build_tools = ["rebar3"], requirements = [], otp_app = "thoas", source = "hex", outer_checksum = "E38697EDFFD6E91BD12CEA41B155115282630075C2A727E7A6B2947F5408B86A" }, 14 + ] 15 + 16 + [requirements] 17 + formz = { path = "../formz" } 18 + gleam_stdlib = { version = ">= 0.34.0 and < 2.0.0" } 19 + gleeunit = { version = ">= 1.0.0 and < 2.0.0" } 20 + lustre = { version = ">= 4.5.1 and < 5.0.0" }
+5
formz_lustre/src/formz_lustre.gleam
··· 1 + import gleam/io 2 + 3 + pub fn main() { 4 + io.println("Hello from formz_lustre!") 5 + }
+22
formz_lustre/src/formz_lustre/fields.gleam
··· 1 + import formz/field 2 + import formz_lustre/widgets 3 + 4 + pub fn text_field() { 5 + field.text_field(widgets.text_widget) 6 + } 7 + 8 + pub fn email_field() { 9 + field.email_field(widgets.text_widget) 10 + } 11 + 12 + pub fn integer_field() { 13 + field.integer_field(widgets.text_widget) 14 + } 15 + 16 + pub fn number_field() { 17 + field.number_field(widgets.text_widget) 18 + } 19 + 20 + pub fn boolean_field() { 21 + field.boolean_field(widgets.checkbox_widget) 22 + }
+60
formz_lustre/src/formz_lustre/simple.gleam
··· 1 + import formz/formz_use as formz 2 + import formz/input.{type Input, Input, InvalidInput} 3 + import gleam/list 4 + import gleam/string 5 + import lustre/attribute 6 + import lustre/element 7 + import lustre/element/html.{html} 8 + 9 + pub fn generate_form(form) -> element.Element(msg) { 10 + form 11 + |> formz.get_inputs 12 + |> list.filter(fn(f) { !f.hidden }) 13 + |> list.map(generate_visible_field) 14 + |> element.fragment 15 + // <> { 16 + // form 17 + // |> formz.get_inputs 18 + // |> list.filter(fn(f) { f.hidden }) 19 + // |> list.map(generate_hidden_field) 20 + // |> string.join("\n") 21 + // } 22 + } 23 + 24 + pub fn generate_visible_field( 25 + f: Input(element.Element(msg)), 26 + ) -> element.Element(msg) { 27 + let label_el = html.label([], [html.text(f.label), html.text(": ")]) 28 + 29 + let description_el = case string.is_empty(f.help_text) { 30 + True -> element.none() 31 + False -> html.span([attribute.class("help_text")], [html.text(f.help_text)]) 32 + } 33 + let widget_el = html.span([attribute.class("widget")], [f.render(f)]) 34 + 35 + let errors_el = case f { 36 + Input(..) -> element.none() 37 + InvalidInput(error:, ..) -> 38 + html.span([attribute.class("errors")], [html.text(error)]) 39 + } 40 + 41 + html.p([attribute.class("simple_field")], [ 42 + label_el, 43 + description_el, 44 + widget_el, 45 + errors_el, 46 + ]) 47 + } 48 + 49 + pub fn generate_hidden_field(f: Input(String)) -> String { 50 + case f.hidden { 51 + False -> "" 52 + True -> { 53 + "<input type=\"hidden\" name=\"" 54 + <> f.name 55 + <> "\" value=\"" 56 + <> f.value 57 + <> "\" />" 58 + } 59 + } 60 + }
+49
formz_lustre/src/formz_lustre/widgets.gleam
··· 1 + import formz/input.{type Input} 2 + import lustre/attribute 3 + import lustre/element 4 + import lustre/element/html 5 + 6 + pub fn checkbox_widget( 7 + input: Input(element.Element(msg)), 8 + ) -> element.Element(msg) { 9 + html.input([ 10 + attribute.type_("checkbox"), 11 + attribute.name(input.name), 12 + attribute.value(input.value), 13 + ]) 14 + } 15 + 16 + pub fn password_widget( 17 + input: Input(element.Element(msg)), 18 + ) -> element.Element(msg) { 19 + html.input([ 20 + attribute.type_("password"), 21 + attribute.name(input.name), 22 + attribute.value(input.value), 23 + ]) 24 + } 25 + 26 + pub fn text_widget(input: Input(element.Element(msg))) -> element.Element(msg) { 27 + let placeholder = "" 28 + 29 + html.input([ 30 + attribute.type_("text"), 31 + attribute.name(input.name), 32 + attribute.value(input.value), 33 + attribute.placeholder(placeholder), 34 + ]) 35 + } 36 + 37 + pub fn textarea_widget( 38 + input: Input(element.Element(msg)), 39 + ) -> element.Element(msg) { 40 + html.textarea([attribute.name(input.name)], "") 41 + } 42 + 43 + pub fn hidden_widget(input: Input(element.Element(msg))) -> element.Element(msg) { 44 + html.input([ 45 + attribute.type_("hidden"), 46 + attribute.name(input.name), 47 + attribute.value(input.value), 48 + ]) 49 + }
+12
formz_lustre/test/formz_lustre_test.gleam
··· 1 + import gleeunit 2 + import gleeunit/should 3 + 4 + pub fn main() { 5 + gleeunit.main() 6 + } 7 + 8 + // gleeunit test functions end in `_test` 9 + pub fn hello_world_test() { 10 + 1 11 + |> should.equal(1) 12 + }
+23
formz_nakai/.github/workflows/test.yml
··· 1 + name: test 2 + 3 + on: 4 + push: 5 + branches: 6 + - master 7 + - main 8 + pull_request: 9 + 10 + jobs: 11 + test: 12 + runs-on: ubuntu-latest 13 + steps: 14 + - uses: actions/checkout@v4 15 + - uses: erlef/setup-beam@v1 16 + with: 17 + otp-version: "26.0.2" 18 + gleam-version: "1.5.0" 19 + rebar3-version: "3" 20 + # elixir-version: "1.15.4" 21 + - run: gleam deps download 22 + - run: gleam test 23 + - run: gleam format --check src test
+4
formz_nakai/.gitignore
··· 1 + *.beam 2 + *.ez 3 + /build 4 + erl_crash.dump
+24
formz_nakai/README.md
··· 1 + # formz_nakai 2 + 3 + [![Package Version](https://img.shields.io/hexpm/v/formz_nakai)](https://hex.pm/packages/formz_nakai) 4 + [![Hex Docs](https://img.shields.io/badge/hex-docs-ffaff3)](https://hexdocs.pm/formz_nakai/) 5 + 6 + ```sh 7 + gleam add formz_nakai@1 8 + ``` 9 + ```gleam 10 + import formz_nakai 11 + 12 + pub fn main() { 13 + // TODO: An example of the project in use 14 + } 15 + ``` 16 + 17 + Further documentation can be found at <https://hexdocs.pm/formz_nakai>. 18 + 19 + ## Development 20 + 21 + ```sh 22 + gleam run # Run the project 23 + gleam test # Run the tests 24 + ```
+21
formz_nakai/gleam.toml
··· 1 + name = "formz_nakai" 2 + version = "1.0.0" 3 + 4 + # Fill out these fields if you intend to generate HTML documentation or publish 5 + # your project to the Hex package manager. 6 + # 7 + # description = "" 8 + # licences = ["Apache-2.0"] 9 + # repository = { type = "github", user = "", repo = "" } 10 + # links = [{ title = "Website", href = "" }] 11 + # 12 + # For a full reference of all the available options, you can have a look at 13 + # https://gleam.run/writing-gleam/gleam-toml/. 14 + 15 + [dependencies] 16 + formz = { path = "../formz" } 17 + gleam_stdlib = ">= 0.34.0 and < 2.0.0" 18 + nakai = ">= 1.0.0 and < 2.0.0" 19 + 20 + [dev-dependencies] 21 + gleeunit = ">= 1.0.0 and < 2.0.0"
+16
formz_nakai/manifest.toml
··· 1 + # This file was generated by Gleam 2 + # You typically do not need to edit this file 3 + 4 + packages = [ 5 + { name = "formz", version = "1.0.0", build_tools = ["gleam"], requirements = ["gleam_stdlib", "justin"], source = "local", path = "../formz" }, 6 + { name = "gleam_stdlib", version = "0.40.0", build_tools = ["gleam"], requirements = [], otp_app = "gleam_stdlib", source = "hex", outer_checksum = "86606B75A600BBD05E539EB59FABC6E307EEEA7B1E5865AFB6D980A93BCB2181" }, 7 + { name = "gleeunit", version = "1.2.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleeunit", source = "hex", outer_checksum = "F7A7228925D3EE7D0813C922E062BFD6D7E9310F0BEE585D3A42F3307E3CFD13" }, 8 + { name = "justin", version = "1.0.1", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "justin", source = "hex", outer_checksum = "7FA0C6DB78640C6DC5FBFD59BF3456009F3F8B485BF6825E97E1EB44E9A1E2CD" }, 9 + { name = "nakai", version = "1.0.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "nakai", source = "hex", outer_checksum = "FD55B7926640FFD21FCC048D7E21752ED94FD424E1A731DC5C9DFB9928007F08" }, 10 + ] 11 + 12 + [requirements] 13 + formz = { path = "../formz" } 14 + gleam_stdlib = { version = ">= 0.34.0 and < 2.0.0" } 15 + gleeunit = { version = ">= 1.0.0 and < 2.0.0" } 16 + nakai = { version = ">= 1.0.0 and < 2.0.0" }
+5
formz_nakai/src/formz_nakai.gleam
··· 1 + import gleam/io 2 + 3 + pub fn main() { 4 + io.println("Hello from formz_nakai!") 5 + }
+22
formz_nakai/src/formz_nakai/fields.gleam
··· 1 + import formz/field 2 + import formz_nakai/widgets 3 + 4 + pub fn text_field() { 5 + field.text_field(widgets.text_widget) 6 + } 7 + 8 + pub fn email_field() { 9 + field.email_field(widgets.text_widget) 10 + } 11 + 12 + pub fn integer_field() { 13 + field.integer_field(widgets.text_widget) 14 + } 15 + 16 + pub fn number_field() { 17 + field.number_field(widgets.text_widget) 18 + } 19 + 20 + pub fn boolean_field() { 21 + field.boolean_field(widgets.checkbox_widget) 22 + }
+57
formz_nakai/src/formz_nakai/simple.gleam
··· 1 + import formz/formz_use as formz 2 + import formz/input.{type Input, Input, InvalidInput} 3 + import gleam/list 4 + import gleam/string 5 + import nakai/attr 6 + import nakai/html 7 + 8 + pub fn generate_form(form) -> html.Node { 9 + form 10 + |> formz.get_inputs 11 + |> list.filter(fn(f) { !f.hidden }) 12 + |> list.map(generate_visible_field) 13 + |> html.Fragment 14 + // <> { 15 + // form 16 + // |> formz.get_inputs 17 + // |> list.filter(fn(f) { f.hidden }) 18 + // |> list.map(generate_hidden_field) 19 + // |> string.join("\n") 20 + // } 21 + } 22 + 23 + pub fn generate_visible_field(f: Input(html.Node)) -> html.Node { 24 + let label_el = html.label([], [html.Text(f.label), html.Text(": ")]) 25 + 26 + let description_el = case string.is_empty(f.help_text) { 27 + True -> html.Nothing 28 + False -> html.span([attr.class("help_text")], [html.Text(f.help_text)]) 29 + } 30 + let widget_el = html.span([attr.class("widget")], [f.render(f)]) 31 + 32 + let errors_el = case f { 33 + Input(..) -> html.Nothing 34 + InvalidInput(error:, ..) -> 35 + html.span([attr.class("errors")], [html.Text(error)]) 36 + } 37 + 38 + html.p([attr.class("simple_field")], [ 39 + label_el, 40 + description_el, 41 + widget_el, 42 + errors_el, 43 + ]) 44 + } 45 + 46 + pub fn generate_hidden_field(f: Input(String)) -> String { 47 + case f.hidden { 48 + False -> "" 49 + True -> { 50 + "<input type=\"hidden\" name=\"" 51 + <> f.name 52 + <> "\" value=\"" 53 + <> f.value 54 + <> "\" />" 55 + } 56 + } 57 + }
+42
formz_nakai/src/formz_nakai/widgets.gleam
··· 1 + import formz/input.{type Input} 2 + import nakai/attr 3 + import nakai/html 4 + 5 + pub fn checkbox_widget(input: Input(html.Node)) -> html.Node { 6 + html.input([ 7 + attr.type_("checkbox"), 8 + attr.name(input.name), 9 + attr.value(input.value), 10 + ]) 11 + } 12 + 13 + pub fn password_widget(input: Input(html.Node)) -> html.Node { 14 + html.input([ 15 + attr.type_("password"), 16 + attr.name(input.name), 17 + attr.value(input.value), 18 + ]) 19 + } 20 + 21 + pub fn text_widget(input: Input(html.Node)) -> html.Node { 22 + let placeholder = "" 23 + 24 + html.input([ 25 + attr.type_("text"), 26 + attr.name(input.name), 27 + attr.value(input.value), 28 + attr.placeholder(placeholder), 29 + ]) 30 + } 31 + 32 + pub fn textarea_widget(input: Input(html.Node)) -> html.Node { 33 + html.textarea([attr.name(input.name)], []) 34 + } 35 + 36 + pub fn hidden_widget(input: Input(html.Node)) -> html.Node { 37 + html.input([ 38 + attr.type_("hidden"), 39 + attr.name(input.name), 40 + attr.value(input.value), 41 + ]) 42 + }
+12
formz_nakai/test/formz_nakai_test.gleam
··· 1 + import gleeunit 2 + import gleeunit/should 3 + 4 + pub fn main() { 5 + gleeunit.main() 6 + } 7 + 8 + // gleeunit test functions end in `_test` 9 + pub fn hello_world_test() { 10 + 1 11 + |> should.equal(1) 12 + }