···11//// A form is a list of fields and a decoder function. This module uses a
22-//// series of callbacks to construct a decoder function as the fields are
22+//// series of callbacks to construct the decoder function as the fields are
33//// being added to the form. The idea is that you'd have a function that
44//// makes the form using the `use` syntax, and then be able to use the form
55//// later for parsing or rendering in different contexts.
···3333//// <td>
3434//// <a href="#definition">definition</a><br>
3535//// <a href="#definition_with_custom_optional">definition_with_custom_optional</a><br>
3636-//// <a href="#transform">transform</a><br>
3737-//// <a href="#transform_with_custom_optional">transform_with_custom_optional</a><br>
3836//// <a href="#verify">verify</a><br>
3937//// <a href="#widget">widget</a>
4038//// </td>
···4543//// <a href="#limit_at_least">limit_at_least</a><br>
4644//// <a href="#limit_at_most">limit_at_most</a><br>
4745//// <a href="#limit_between">limit_between</a><br>
4848-//// <a href="#simple_blank_fields">simple_blank_fields</a>
4646+//// <a href="#simple_limit_check">simple_limit_check</a>
4947//// </td>
5048//// </tr>
5149//// <tr>
5252-//// <td>Accessing and manipulating form fields</td>
5050+//// <td>Accessing and manipulating form items</td>
5351//// <td>
5452//// <a href="#get">get</a><br>
5553//// <a href="#items">items</a><br>
···6765////
6866//// ```gleam
6967//// fn make_form() {
7070-//// use name <- formz.require(field("name"), defintions.text_field())
6868+//// use name <- formz.require(field("name"), definitions.text_field())
7169////
7270//// formz.create_form(name)
7371//// }
···8280////
8381//// ```gleam
8482//// fn make_form() {
8585-//// use greeting <- optional(field("greeting"), defintions.text_field())
8686-//// use name <- optional(field("name"), defintions.text_field())
8383+//// use greeting <- optional(field("greeting"), definitions.text_field())
8484+//// use name <- optional(field("name"), definitions.text_field())
8785////
8886//// formz.create_form(greeting <> " " <> name)
8987//// }
···115113/// been added.
116114pub opaque type Form(widget, output) {
117115 Form(
118118- items: List(FormItem(widget)),
119119- decode: fn(List(FormItem(widget))) -> Result(output, List(FormItem(widget))),
116116+ items: List(Item(widget)),
117117+ decode: fn(List(Item(widget))) -> Result(output, List(Item(widget))),
120118 stub: output,
121119 )
122120}
123121124124-/// A form is a decode function and a list of `FormItem`s. You add `FormItem`s
125125-/// to a form using the `optional`, `require`, `list`, etc functions. You
126126-/// primarily only use these directly when writing a form generator function to
127127-/// output your function to HTML.
122122+/// You add an `Item` to a form using the `optional`, `require`, `list`,
123123+/// `limited_list` and `subform` functions. A form is a list of `Item`s and
124124+/// each item is parsed to a single value, which the decode function will
125125+/// choose how to use.
126126+///
127127+/// You primarily only use an `Item` directly when writing a form generator
128128+/// function to output your function to HTML.
128129///
129129-/// You can also manipulate these after a form has been created, to change
130130+/// You can also manipulate an `Item` after a form has been created, to change
130131/// things like labels, help text, etc. There are specific functions,
131132/// `update_field`, `update_listfield` and `update_subform`, to help with this,
132133/// so you don't have to pattern match when updating a specific item.
···135136/// let form = make_form()
136137/// form.update_field("name", field.set_label(_, "Full Name"))
137138/// ```
138138-pub type FormItem(widget) {
139139+pub type Item(widget) {
139140 /// A single field that (generally speaking) corresponds to a single
140141 /// HTML input
141141- Field(detail: field.Field, state: FieldState, widget: widget)
142142+ Field(detail: field.Field, state: InputState, widget: widget)
142143 /// A single field that a consumer can submit multiple values for.
143144 ListField(
144145 detail: field.Field,
145145- states: List(FieldState),
146146- blank_fields: fn(Int) -> List(FieldState),
146146+ states: List(InputState),
147147+ limit_check: LimitCheck,
147148 widget: widget,
148149 )
149150 /// A group of fields that are added as and then parsed to a single unit.
150150- SubForm(detail: subform.SubForm, items: List(FormItem(widget)))
151151+ SubForm(detail: subform.SubForm, items: List(Item(widget)))
151152}
152153153153-/// The state of the field, this is used to track the current raw value,
154154-/// whether the field is required or not, if the field has been validated,
155155-/// and the outcome of that validation.
156156-pub type FieldState {
157157- Unvalidated(value: String, presence: FieldPresence)
158158- Valid(value: String, presence: FieldPresence)
159159- Invalid(value: String, presence: FieldPresence, error: String)
154154+/// The state of the an input for a field. This is used to track the current
155155+/// raw value, whether a value is required or not, if the value has been
156156+/// validated, and the outcome of that validation.
157157+pub type InputState {
158158+ Unvalidated(value: String, requirement: Requirement)
159159+ Valid(value: String, requirement: Requirement)
160160+ Invalid(value: String, requirement: Requirement, error: String)
160161}
161162162162-/// Whether a field is required or not.
163163-pub type FieldPresence {
163163+/// Whether an input value is required for an input field.
164164+pub type Requirement {
164165 Optional
165166 Required
166167}
167168168168-/// A `Definition` is the second argument needed to [add a field to a form](#require).
169169-/// It is what describes how a field works, e.g. how it looks and how it's
170170-/// parsed. It is the heavy compared to the lightness of a [Field](https://hexdocs.pm/formz/formz/field.html);
171171-/// they take a bit more work to make as they are intended to be reusable.
169169+/// A `Definition` describes how a field works, e.g. how it looks and how it's
170170+/// parsed. It is the heavy compared to the lightness of a
171171+/// [Field](https://hexdocs.pm/formz/formz/field.html);
172172+/// definitions take a bit more work to make as they are intended to be reusable.
172173///
173173-/// The first role of a `Defintion` is to generate the HTML widget for the field.
174174-/// This library is format-agnostic and you can generate HTML widgets as raw
175175-/// strings, Lustre elements, Nakai nodes, something else, etc. There are
176176-/// currently three `formz` libraries that provide common field
177177-/// definitions (and widgets) for the most common HTML inputs.
174174+/// The first role of a `Defintion` is to generate the HTML input for the field.
175175+/// This library is format-agnostic and you can generate inputs as raw
176176+/// strings, Lustre elements, Nakai nodes, something else, etc. The second role
177177+/// of a `Definition` is to parse the raw string data from the input into a
178178+/// Gleam type.
179179+///
180180+/// There are currently three `formz` libraries that provide common field
181181+/// definitions for the most common HTML inputs:
178182///
179183/// - [formz_string](https://hexdocs.pm/formz_string/)
180184/// - [formz_nakai](https://hexdocs.pm/formz_nakai/)
181181-/// - [formz_lustre](https://hexdocs.pm/formz_lustre/) (untested in a browser,
182182-/// would it be useful there??)
185185+/// - [formz_lustre](https://hexdocs.pm/formz_lustre/)
183186///
184184-/// The second role of a `Definition` is to parse the data from the field.
187187+/// How a definition parses an input value depends on whether a value is required
188188+/// for that input (i.e. whether `optional`, `require`, `list`, or `limited_list`
189189+/// was used to add it to the form). If a value is required, the definition is
190190+/// expected to return a string error if the input is empty, or the `required` type
191191+/// if it isn't. You can use the `definition` function to create a simple
192192+/// definition that just parses to an `Option` if an input is empty.
193193+///
194194+/// However, not all fields should be parsed into an `Option` when
195195+/// given an empty input value. For example, an optional text field might be an
196196+/// empty string or an optional checkbox might be False. For these cases, you
197197+/// can use the `definition_with_custom_optional` function to create a definition
198198+/// that can parse to any type when the input is empty.
185199pub opaque type Definition(widget, required, optional) {
186200 Definition(
187201 widget: widget,
···202216 )
203217}
204218219219+/// When adding a list field to a form with `limited_list`, you have to provide
220220+/// a `LimitCheck` function that checks the number of inputs (and associated
221221+/// values) for the field. This function can either say the number of inputs
222222+/// is Ok and optionally add more, or say the number of inputs was too high
223223+/// and return an error. For example, you are presenting a blank form to a
224224+/// consumer, and you want to show three initial fields for them to fill out,
225225+/// or you want to always show one more additional field than the number of
226226+/// values that have already belong to the form, etc.
227227+///
228228+/// There are helper functions, `limit_at_least`, `limit_at_most`, and
229229+/// `limit_between` or more generally `simple_limit_check` to make a
230230+/// `LimitCheck` function for you. I would imagine that those will cover 99.9%
231231+/// of cases and almost no one will need to write their own `LimitCheck`. But
232232+/// if you do, look at the source for `simple_limit_check` for a better idea
233233+/// of how to write one.
234234+///
235235+/// This function takes as its only argument, the number of fields that already
236236+/// have a value. It should return a list of `Unvalidated` `InputState` items
237237+/// that specify if the value is required or not.
238238+///
239239+/// This is used multiple times... when the form is created so we know how many
240240+/// initial inputs to present, when data is added so we know if we need to add
241241+/// more inputs so users can add more items, and when the form is decoded and
242242+/// we are checking if too many fields have been added.
243243+pub type LimitCheck =
244244+ fn(Int) -> Result(List(InputState), Int)
245245+205246type ListParsingResult(input_output) {
206247 ListParsingResult(
207248 value: String,
208208- presence: FieldPresence,
249249+ requirement: Requirement,
209250 output: Result(input_output, String),
210251 )
211252}
212253213213-/// Create an empty form that only parses to `thing`. This is primarily
254254+/// Create an empty form that "decodes" directly to `thing`. This is
214255/// intended to be the final return value of a chain of callbacks that adds
215256/// the form's fields.
216257///
···246287/// callback should be a function without side effects.** It can be called any
247288/// number of times. Don't do anything but create the type with the data you
248289/// need! If you need to do decoding that has side effects, you should use
249249-/// `decode_then_try` instead.
290290+/// `decode_then_try`.
250291pub fn optional(
251292 field: field.Field,
252293 definition: Definition(widget, _, input_output),
···276317/// callback should be a function without side effects.** It can be called any
277318/// number of times. Don't do anything but create the type with the data you
278319/// need! If you need to do decoding that has side effects, you should use
279279-/// `decode_then_try` instead.
320320+/// `decode_then_try`.
280321pub fn require(
281322 field: field.Field,
282323 is definition: Definition(widget, required_output, _),
···294335295336fn add_field(
296337 field: field.Field,
297297- required: FieldPresence,
338338+ requirement: Requirement,
298339 widget: widget,
299340 parse_field: fn(String) -> Result(input_output, String),
300341 stub: input_output,
···307348308349 // prepend the new field to the items from the form we got in the previous step.
309350 let updated_items = [
310310- Field(field, Unvalidated("", required), widget),
351351+ Field(field, Unvalidated("", requirement), widget),
311352 ..next_form.items
312353 ]
313354314355 // now create the decode function. decode function accepts most recent
315356 // version of input list, since data can be added to it. the list
316357 // above we just needed for the initial setup.
317317- let decode = fn(items: List(FormItem(widget))) {
358358+ let decode = fn(items: List(Item(widget))) {
318359 // pull out the latest version of this field to get latest input data
319360 let assert [Field(field, state, widget), ..next_items] = items
320361···336377 // form has errors, but this field was good, so add it to the list
337378 // of fields as is.
338379 Error(error_items), Ok(_) -> {
339339- let f = Field(field, Valid(state.value, required), widget)
380380+ let f = Field(field, Valid(state.value, requirement), widget)
340381 Error([f, ..error_items])
341382 }
342383···344385 // mark this field as invalid, mark all the existing fields as valid,
345386 // and return all the fields we've got so far
346387 Ok(_), Error(error) -> {
347347- let f = Field(field, Invalid(state.value, required, error), widget)
388388+ let f = Field(field, Invalid(state.value, requirement, error), widget)
348389 Error([f, ..mark_all_fields_as_valid(next_items)])
349390 }
350391351392 // form already has errors and this field errored, so mark this field
352393 // as invalid, and add it to the list of errors
353394 Error(error_items), Error(error) -> {
354354- let f = Field(field, Invalid(state.value, required, error), widget)
395395+ let f = Field(field, Invalid(state.value, requirement, error), widget)
355396 Error([f, ..error_items])
356397 }
357398 }
···359400 Form(items: updated_items, decode:, stub: next_form.stub)
360401}
361402362362-pub fn simple_blank_fields(
363363- min: Int,
364364- max: Int,
365365- extra: Int,
366366-) -> fn(Int) -> List(FieldState) {
403403+/// Convenience function for creating a `LimitCheck` function. This takes
404404+/// the minimum number of required values, the maximum number of allowed values,
405405+/// and the number of "extra" blank inputs that should be offered to the user
406406+/// for filling out.
407407+///
408408+/// If "extra" is 0, (say, to manage blank fields via javascript), then this
409409+/// will show 1 blank field initially.
410410+pub fn simple_limit_check(min: Int, max: Int, extra: Int) -> LimitCheck {
367411 fn(num_nonempty) {
368368- int.max(1 - num_nonempty, int.min(extra, max - num_nonempty))
369369- list.fold([1, extra, min], 0, int.max)
412412+ case max - num_nonempty {
413413+ x if x < 0 -> Error(x)
414414+ _ -> {
415415+ int.max(1 - num_nonempty, int.min(extra, max - num_nonempty))
416416+ list.fold([1, extra, min], 0, int.max)
370417371371- // if they've specified a minimum required, then start there
372372- let num_required = int.max(min - num_nonempty, 0)
418418+ // if they've specified a minimum required, then start there
419419+ let num_required = int.max(min - num_nonempty, 0)
373420374374- // at least one field needs to be present. don't want a form that is asking
375375- // for no input
376376- let num_base = case num_nonempty, num_required {
377377- x, y if x > 0 || y > 0 -> 0
378378- _, _ -> 1
379379- }
421421+ // at least one field needs to be present. don't want a form that is asking
422422+ // for no input
423423+ let num_base = case num_nonempty, num_required {
424424+ x, y if x > 0 || y > 0 -> 0
425425+ _, _ -> 1
426426+ }
380427381381- // they've asked for extra fields, add those unless doing so would
382382- // exceed the max
383383- let num_extra = int.min(extra, max - num_nonempty)
428428+ // they've asked for extra fields, add those unless doing so would
429429+ // exceed the max
430430+ let num_extra = int.min(extra, max - num_nonempty)
384431385385- // take whatever's bigger for our optional ones, either the bare minimum
386386- // (base) or the extra if they've got room for it and they've asked for it
387387- let num_optional = int.max(num_base, num_extra) - num_required
432432+ // take whatever's bigger for our optional ones, either the bare minimum
433433+ // (base) or the extra if they've got room for it and they've asked for it
434434+ let num_optional = int.max(num_base, num_extra) - num_required
388435389389- list.append(
390390- list.repeat(Unvalidated("", Required), num_required),
391391- list.repeat(Unvalidated("", Optional), num_optional),
392392- )
436436+ Ok(list.append(
437437+ list.repeat(Unvalidated("", Required), num_required),
438438+ list.repeat(Unvalidated("", Optional), num_optional),
439439+ ))
440440+ }
441441+ }
393442 }
394443}
395444396396-/// Convenience function for creating a `BlankFieldsFunc` with a minimum number
445445+/// Convenience function for creating a `LimitCheck` with a minimum number
397446/// of required values. This sets the maximum to `1,000,000`, effectively unlimited.
398398-pub fn limit_at_least(min: Int) {
399399- simple_blank_fields(min, 1_000_000, 1)
447447+pub fn limit_at_least(min: Int) -> LimitCheck {
448448+ simple_limit_check(min, 1_000_000, 1)
400449}
401450402402-/// Convenience function for creating a `BlankFieldsFunc` with a maximum number
451451+/// Convenience function for creating a `LimitCheck` with a maximum number
403452/// of accepted values. This sets the minimum to `0`.
404404-pub fn limit_at_most(max: Int) {
405405- simple_blank_fields(0, max, 1)
453453+pub fn limit_at_most(max: Int) -> LimitCheck {
454454+ simple_limit_check(0, max, 1)
406455}
407456408408-/// Convenience function for creating a `BlankFieldsFunc` with a minimum and maximum
457457+/// Convenience function for creating a `LimitCheck` with a minimum and maximum
409458/// number of values.
410410-pub fn limit_between(min: Int, max: Int) {
411411- simple_blank_fields(min, max, 1)
459459+pub fn limit_between(min: Int, max: Int) -> LimitCheck {
460460+ simple_limit_check(min, max, 1)
412461}
413462414463/// Add a list field to a form, but with limits on the number of values that
415415-/// can be submitted.
416416-///
417417-/// When adding a list field to a form, you have to provide a function that
418418-/// tells the form if and how many blank fields to provide for the
419419-/// consumer to fill out.
420420-///
421421-/// For example, you are presenting an empty form to a consumer, and you
422422-/// want to show three blank fields for them to fill out, or you want to always
423423-/// show one more blank field than the number of values that already belong to
424424-/// the form, etc.
425425-///
426426-/// This function takes as its only argument, the number of fields that already
427427-/// have a value. It should return a list of `Unvalidated` `FieldState` items
428428-/// that specify if the value is required or not.
429429-///
430430-/// There are helper functions, `limit_at_least`, `limit_at_most`, and
431431-/// `limit_between` or more generally `simple_blank_fields` for the most common
432432-/// use cases.
464464+/// can be submitted. The `limit_check` function is used to impose those
465465+/// limits, and the `limit_at_least`, `limit_at_most`, and `limit_between`
466466+/// functions help you create this function for the most likely scenarios.
433467///
434468/// The final argument is a callback that will be called when the form
435469/// is being... constructed to look for more fields; validated to check for
···437471/// callback should be a function without side effects.** It can be called any
438472/// number of times. Don't do anything but create the type with the data you
439473/// need! If you need to do decoding that has side effects, you should use
440440-/// `decode_then_try` instead.
474474+/// `decode_then_try`.
475475+///
476476+///### Example
477477+///
478478+/// ```gleam
479479+/// fn make_form() {
480480+/// use names <- formz.limited_list(formz.limit_at_most(4), field("name"), definitions.text_field())
481481+/// // names is a List(String)
482482+/// formz.create_form(name)
483483+/// }
441484pub fn limited_list(
442442- blank_fields: fn(Int) -> List(FieldState),
485485+ limit_check: fn(Int) -> Result(List(InputState), Int),
443486 field: field.Field,
444487 is definition: Definition(widget, required_output, _),
445488 next next: fn(List(required_output)) -> Form(widget, form_output),
···449492 // from the form.
450493 let next_form = next([definition.stub])
451494495495+ let initial_fields = limit_check(0) |> result.unwrap([])
452496 // prepend the new field to the items from the form we got in the previous step.
453497 let updated_items = [
454454- ListField(field, blank_fields(0), blank_fields, definition.widget),
498498+ ListField(field, initial_fields, limit_check, definition.widget),
455499 ..next_form.items
456500 ]
457501458502 // now create the decode function. decode function accepts most recent
459503 // version of input list, since data can be added to it. the list
460504 // above we just needed for the initial setup.
461461- let decode = fn(items: List(FormItem(widget))) {
505505+ let decode = fn(items: List(Item(widget))) {
462506 // pull out the latest version of this field to get latest input data
463463- let assert [ListField(field, states, blank_fields, widget), ..next_items] =
507507+ let assert [ListField(field, states, limit_check, widget), ..next_items] =
464508 items
465509466510 // go through all decode all input values. these can have empty rows
···489533490534 // form has errors, but this field was good, so mark all states as valid
491535 Error(error_items), Ok(_) -> {
492492- let states = states |> list.map(fn(s) { Valid(s.value, s.presence) })
493493- let f = ListField(field, states, blank_fields, widget)
536536+ let states = states |> list.map(fn(s) { Valid(s.value, s.requirement) })
537537+ let f = ListField(field, states, limit_check, widget)
494538 Error([f, ..error_items])
495539 }
496540···499543 // valid, and then return all these fields we've got so far
500544 Ok(_), Error(_) -> {
501545 let states = list.map(item_results, state_from_parse_result)
502502- let f = ListField(field, states, blank_fields, widget)
546546+ let f = ListField(field, states, limit_check, widget)
503547 Error([f, ..mark_all_fields_as_valid(next_items)])
504548 }
505549···507551 // to the list of errors, but first marking the invalid states as... invalid
508552 Error(error_items), Error(_) -> {
509553 let states = list.map(item_results, state_from_parse_result)
510510- let f = ListField(field, states, blank_fields, widget)
554554+ let f = ListField(field, states, limit_check, widget)
511555 Error([f, ..error_items])
512556 }
513557 }
···518562519563fn state_from_parse_result(
520564 result: ListParsingResult(input_output),
521521-) -> FieldState {
522522- let ListParsingResult(value, presence, output) = result
565565+) -> InputState {
566566+ let ListParsingResult(value, requirement, output) = result
523567 case output {
524524- Ok(_) -> Valid(value, presence)
525525- Error(error) -> Invalid(value, presence, error)
568568+ Ok(_) -> Valid(value, requirement)
569569+ Error(error) -> Invalid(value, requirement, error)
526570 }
527571}
528572···530574 states: List(ListParsingResult(output)),
531575) -> List(Result(output, String)) {
532576 list.filter_map(states, fn(result) {
533533- case result.value, result.presence {
577577+ case result.value, result.requirement {
534578 "", Optional -> Error(Nil)
535579 _, _ -> Ok(result.output)
536580 }
···538582}
539583540584fn parse_list_state(
541541- state: FieldState,
585585+ state: InputState,
542586 parse: fn(String) -> Result(a, String),
543587 stub: a,
544588) -> ListParsingResult(a) {
545545- case state.value, state.presence {
589589+ case state.value, state.requirement {
546590 "", Required -> parse(state.value)
547591 "", Optional -> Ok(stub)
548592 _, _ -> parse(state.value)
549593 }
550550- |> ListParsingResult(state.value, state.presence, _)
594594+ |> ListParsingResult(state.value, state.requirement, _)
551595}
552596553597/// Add a list field to a form, but with no limits on the number of values that
···560604/// callback should be a function without side effects.** It can be called any
561605/// number of times. Don't do anything but create the type with the data you
562606/// need! If you need to do decoding that has side effects, you should use
563563-/// `decode_then_try` instead.
607607+/// `decode_then_try`.
564608pub fn list(
565609 field: field.Field,
566610 is definition: Definition(widget, required_output, _),
···569613 limited_list(limit_at_most(1_000_000), field, definition, next)
570614}
571615572572-fn add_prefix_to_item(
573573- item: FormItem(widget),
574574- prefix: String,
575575-) -> FormItem(widget) {
616616+fn add_prefix_to_item(item: Item(widget), prefix: String) -> Item(widget) {
576617 case item {
577618 Field(item_details, state, widget) -> {
578619 let name = prefix <> "." <> item_details.name
579620 Field(item_details |> field.set_name(name), state, widget)
580621 }
581581- ListField(item_details, states, blank_fields, widget) -> {
622622+ ListField(item_details, states, limit_check, widget) -> {
582623 let name = prefix <> "." <> item_details.name
583624 ListField(
584625 item_details |> field.set_name(name),
585626 states,
586586- blank_fields,
627627+ limit_check,
587628 widget,
588629 )
589630 }
···605646/// callback should be a function without side effects.** It can be called any
606647/// number of times. Don't do anything but create the type with the data you
607648/// need! If you need to do decoding that has side effects, you should use
608608-/// `decode_then_try` instead.
649649+/// `decode_then_try`.
609650pub fn subform(
610651 subform: subform.SubForm,
611652 form: Form(widget, sub_output),
···617658 let subform = SubForm(subform, sub_items)
618659 let updated_items = [subform, ..next_form.items]
619660620620- let decode = fn(items: List(FormItem(widget))) {
661661+ let decode = fn(items: List(Item(widget))) {
621662 // pull out the latest version of this field to get latest input data
622663 let assert [SubForm(details, sub_items), ..next_items] = items
623664···657698}
658699659700/// Add input data to this form. This will set the raw string value of the fields.
660660-/// It does not trigger any parsing, so you can also use this to set default values
661661-/// (if you do it in your form generator function) or initial values (if you do it
662662-/// before rendering an empty form).
701701+/// It does not trigger any parsing or decoding, so you can also use this to set
702702+/// default values (if you do it in your form generator function) or initial values
703703+/// (if you do it before rendering a blank form).
663704///
664705/// The input data is a list of tuples, where the first element is the name of the
665706/// field and the second element is the value to set. If the field does not exist
···677718}
678719679720fn do_data(
680680- items: List(FormItem(widget)),
721721+ items: List(Item(widget)),
681722 data: Dict(String, List(String)),
682682-) -> List(FormItem(widget)) {
723723+) -> List(Item(widget)) {
683724 list.map(items, fn(item) {
684725 let values = dict.get(data, get_item_name(item))
685726 case item, values {
686727 Field(detail, state, widget), Ok([_, ..] as values) -> {
687728 let assert Ok(last) = list.last(values)
688688- Field(detail, Unvalidated(last, state.presence), widget)
729729+ Field(detail, Unvalidated(last, state.requirement), widget)
689730 }
690690- ListField(detail, states, blank_fields, widget), Ok(values) -> {
731731+ ListField(detail, states, limit_check, widget), Ok(values) -> {
691732 let nonempty = list.filter(values, fn(v) { !string.is_empty(v) })
692733 let num_nonempty = list.length(nonempty)
693734735735+ let additional_fields = limit_check(num_nonempty) |> result.unwrap([])
694736 let items =
695695- list.map2(states, nonempty, fn(s, v) { Unvalidated(v, s.presence) })
737737+ list.map2(states, nonempty, fn(s, v) { Unvalidated(v, s.requirement) })
696738 |> list.append(
697739 nonempty
698740 |> list.drop(list.length(states))
699741 |> list.map(fn(v) { Unvalidated(v, Optional) }),
700742 )
701701- |> list.append(blank_fields(num_nonempty))
743743+ |> list.append(additional_fields)
702744703703- ListField(detail, items, blank_fields, widget)
745745+ ListField(detail, items, limit_check, widget)
704746 }
705747 SubForm(detail, items), _ -> SubForm(detail, do_data(items, data))
706748 _, _ -> item
···738780 }
739781}
740782741741-/// Parse the form, then apply a function to the output if it was successful.
783783+/// Decode the form, then apply a function to the output if it was successful.
742784/// This is a very thin wrapper around `decode` and `result.try`, but the
743785/// difference being it will pass the form along to the function as a second
744786/// argument in addition to the successful result. This allows you to easily
···774816 })
775817}
776818777777-fn mark_all_fields_as_valid(
778778- items: List(FormItem(widget)),
779779-) -> List(FormItem(widget)) {
819819+fn mark_all_fields_as_valid(items: List(Item(widget))) -> List(Item(widget)) {
780820 list.map(items, fn(item) {
781821 case item {
782822 Field(field, state, widget) ->
783783- Field(field, Valid(state.value, state.presence), widget)
784784- ListField(field, states, blank_fields, widget) -> {
823823+ Field(field, Valid(state.value, state.requirement), widget)
824824+ ListField(field, states, limit_check, widget) -> {
785825 let new_states =
786826 states
787787- |> list.map(fn(state) { Valid(state.value, state.presence) })
788788- ListField(field, new_states, blank_fields, widget)
827827+ |> list.map(fn(state) { Valid(state.value, state.requirement) })
828828+ ListField(field, new_states, limit_check, widget)
789829 }
790830 SubForm(subform, items) ->
791831 SubForm(subform, mark_all_fields_as_valid(items))
···846886 form.items |> list.map(get_item_name) |> validate(form, _)
847887}
848888849849-/// Get each [`FormItem`](https://hexdocs.pm/formz/formz.html#FormItem) added
850850-/// to the form. Any time a field, list field, or subform are added, a FormItem is created.
851851-pub fn items(form: Form(widget, output)) -> List(FormItem(widget)) {
889889+/// Get each [`Item`](https://hexdocs.pm/formz/formz.html#Item) added
890890+/// to the form. Any time a field, list field, or subform are added, a `Item`
891891+/// is created. Use this to loop through all the fields of your form and
892892+/// generate HTML for them.
893893+pub fn items(form: Form(widget, output)) -> List(Item(widget)) {
852894 form.items
853895}
854896855855-/// Get the [`FormItem`](https://hexdocs.pm/formz/formz.html#FormItem) with the
897897+/// Get the [`Item`](https://hexdocs.pm/formz/formz.html#Item) with the
856898/// given name. If multiple items have the same name, the first one is returned.
857899pub fn get(
858900 form: Form(widget, output),
859901 name: String,
860860-) -> Result(FormItem(widget), Nil) {
902902+) -> Result(Item(widget), Nil) {
861903 list.find(form.items, fn(item) { name == get_item_name(item) })
862904}
863905864864-/// Update the [`FormItem`](https://hexdocs.pm/formz/formz.html#FormItem) with
906906+/// Update the [`Item`](https://hexdocs.pm/formz/formz.html#Item) with
865907/// the given name using the provided function. If multiple items have the same
866908/// name, it will be called on all of them.
867909pub fn update(
868910 form: Form(widget, output),
869911 name: String,
870870- fun: fn(FormItem(widget)) -> FormItem(widget),
912912+ fun: fn(Item(widget)) -> Item(widget),
871913) {
872914 let items = do_update(form.items, name, fun)
873915 Form(..form, items:)
874916}
875917876918fn do_update(
877877- items: List(FormItem(widget)),
919919+ items: List(Item(widget)),
878920 name: String,
879879- fun: fn(FormItem(widget)) -> FormItem(widget),
880880-) -> List(FormItem(widget)) {
921921+ fun: fn(Item(widget)) -> Item(widget),
922922+) -> List(Item(widget)) {
881923 list.map(items, fn(item) {
882924 case item {
883925 Field(detail, _, _) if detail.name == name -> fun(item)
···894936 })
895937}
896938897897-/// Update the [`Field`](https://hexdocs.pm/formz/formz/field.html) with
939939+/// Update the `Field` [details](https://hexdocs.pm/formz/formz/field.html) with
898940/// the given name using the provided function. If multiple items have the same
899941/// name, it will be called on all of them. If no items have the given name,
900942/// or an item with the given name exists but isn't a `Field`, this function
···902944///
903945/// ```gleam
904946/// let form = make_form()
905905-/// update(form, "name", field.set_label(_, "Full Name"))
947947+/// update_field(form, "name", field.set_label(_, "Full Name"))
906948/// ```
907949pub fn update_field(
908950 form: Form(widget, output),
···917959 })
918960}
919961920920-/// Update the [`ListField`](https://hexdocs.pm/formz/formz/field.html) with
962962+/// Update the `ListField` [details]](https://hexdocs.pm/formz/formz/field.html) with
921963/// the given name using the provided function. If multiple items have the same
922964/// name, it will be called on all of them. If no items have the given name,
923965/// or an item with the given name exists but isn't a `ListField`, this function
···9631005 })
9641006}
965100710081008+/// Convenience function for setting the `InputState` of a field to
10091009+/// Invalid with a given error message.
10101010+///
10111011+/// ### Example
10121012+///
10131013+/// ```
10141014+/// set_field_error(form, "username", "Username is taken")
10151015+/// ```
9661016pub fn set_field_error(
9671017 form: Form(widget, output),
9681018 name: String,
···9711021 update(form, name, fn(item) {
9721022 case item {
9731023 Field(field, state, widget) ->
974974- Field(field, Invalid(state.value, state.presence, str), widget)
10241024+ Field(field, Invalid(state.value, state.requirement, str), widget)
9751025 _ -> item
9761026 }
9771027 })
9781028}
979102910301030+/// Convenience function for setting the `InputState`s of a list field. This
10311031+/// takes a list of Results, where the Ok means the input is `Valid` and
10321032+/// `Error` means the input is `Invalid` with the given error message.
10331033+///
10341034+/// ### Example
10351035+///
10361036+/// ```
10371037+/// set_listfield_errors(form, "pet_names", [Ok(Nil), Ok(Nil), Error("Must be a cat")])
10381038+/// ```
9801039pub fn set_listfield_errors(
9811040 form: Form(widget, output),
9821041 name: String,
···9841043) -> Form(widget, output) {
9851044 update(form, name, fn(item) {
9861045 case item {
987987- ListField(field, states, blank_fields, widget) -> {
10461046+ ListField(field, states, limit_check, widget) -> {
9881047 let invalid_states =
9891048 list.map2(states, errors, fn(state, err) {
9901049 case err {
991991- Error(str) -> Invalid(state.value, state.presence, str)
10501050+ Error(str) -> Invalid(state.value, state.requirement, str)
9921051 Ok(Nil) -> state
9931052 }
9941053 })
995995- ListField(field, invalid_states, blank_fields, widget)
10541054+ ListField(field, invalid_states, limit_check, widget)
9961055 }
9971056 _ -> item
9981057 }
9991058 })
10001059}
1001106010021002-fn get_item_name(item: FormItem(widget)) -> String {
10611061+fn get_item_name(item: Item(widget)) -> String {
10031062 case item {
10041063 Field(field, _, _) -> field.name
10051064 ListField(field, _, _, _) -> field.name
···10081067}
1009106810101069/// Create a simple `Definition` that is parsed as an `Option` if the field
10111011-/// is empty.
10701070+/// is empty. See [formz_string](https://hexdocs.pm/formz_string/formz_string/definitions.html)
10711071+/// for more examples of making widgets and definitions.
10121072pub fn definition(
10131073 widget widget: widget,
10141074 parse parse: fn(String) -> Result(required, String),
···10281088 )
10291089}
1030109010911091+/// Create a `Definition` that can parse to any type if the field is optional.
10921092+/// This takes two functions. The first, `parse`, is the "required"" parse
10931093+/// function, which takes the raw string value, and turns it into the required
10941094+/// type. The second, `optional_parse`, is a function that takes the normal
10951095+/// parse function and the raw string value, and it is supposed to check the
10961096+/// input string: if it is empty, return an `Ok` with the `optional_stub`
10971097+/// value; and if it's not empty use the normal parse function.
10981098+///
10991099+/// See [formz_string](https://hexdocs.pm/formz_string/formz_string/definitions.html)
11001100+/// for more examples of making widgets and definitions.
10311101pub fn definition_with_custom_optional(
10321102 widget widget: widget,
10331103 parse parse: fn(String) -> Result(required, String),
···10641134 Definition(..def, parse: fn(val) { val |> def.parse |> result.try(fun) })
10651135}
1066113610671067-pub fn transform(
10681068- def: Definition(widget, old_required, old_optional),
10691069- transform: fn(old_required) -> Result(new_required, String),
10701070- stub: new_required,
10711071-) -> Definition(widget, new_required, option.Option(new_required)) {
10721072- let Definition(widget, parse, _, _, _) = def
10731073- Definition(
10741074- widget:,
10751075- parse: fn(val) { val |> parse |> result.try(transform) },
10761076- stub:,
10771077- optional_parse: fn(parse, str) {
10781078- case str {
10791079- "" -> Ok(option.None)
10801080- _ -> parse(str) |> result.map(option.Some)
10811081- }
10821082- },
10831083- optional_stub: option.None,
10841084- )
10851085-}
11371137+// pub fn transform(
11381138+// def: Definition(widget, old_required, old_optional),
11391139+// transform: fn(old_required) -> Result(new_required, String),
11401140+// stub: new_required,
11411141+// ) -> Definition(widget, new_required, option.Option(new_required)) {
11421142+// let Definition(widget, parse, _, _, _) = def
11431143+// Definition(
11441144+// widget:,
11451145+// parse: fn(val) { val |> parse |> result.try(transform) },
11461146+// stub:,
11471147+// optional_parse: fn(parse, str) {
11481148+// case str {
11491149+// "" -> Ok(option.None)
11501150+// _ -> parse(str) |> result.map(option.Some)
11511151+// }
11521152+// },
11531153+// optional_stub: option.None,
11541154+// )
11551155+// }
1086115610871087-pub fn transform_with_custom_optional(
10881088- def: Definition(widget, old_required, old_optional),
10891089- transform: fn(old_required) -> Result(new_required, String),
10901090- stub: new_required,
10911091- optional_parse: fn(fn(String) -> Result(new_required, String), String) ->
10921092- Result(new_optional, String),
10931093- optional_stub: new_optional,
10941094-) -> Definition(widget, new_required, new_optional) {
10951095- let Definition(widget, parse, _, _, _) = def
10961096- Definition(
10971097- widget:,
10981098- parse: fn(val) { val |> parse |> result.try(transform) },
10991099- stub:,
11001100- optional_parse:,
11011101- optional_stub:,
11021102- )
11031103-}
11571157+// pub fn transform_with_custom_optional(
11581158+// def: Definition(widget, old_required, old_optional),
11591159+// transform: fn(old_required) -> Result(new_required, String),
11601160+// stub: new_required,
11611161+// optional_parse: fn(fn(String) -> Result(new_required, String), String) ->
11621162+// Result(new_optional, String),
11631163+// optional_stub: new_optional,
11641164+// ) -> Definition(widget, new_required, new_optional) {
11651165+// let Definition(widget, parse, _, _, _) = def
11661166+// Definition(
11671167+// widget:,
11681168+// parse: fn(val) { val |> parse |> result.try(transform) },
11691169+// stub:,
11701170+// optional_parse:,
11711171+// optional_stub:,
11721172+// )
11731173+// }
1104117411751175+/// Update the widget of a definition.
11051176pub fn widget(
11061177 def: Definition(widget, a, b),
11071178 widget: widget,
···11241195}
1125119611261197@internal
11271127-pub fn get_states(form: Form(widget, output)) -> List(FieldState) {
11981198+pub fn get_states(form: Form(widget, output)) -> List(InputState) {
11281199 form.items |> do_get_states |> list.reverse
11291200}
1130120111311131-fn do_get_states(items: List(FormItem(widget))) -> List(FieldState) {
12021202+fn do_get_states(items: List(Item(widget))) -> List(InputState) {
11321203 list.fold(items, [], fn(acc, item) {
11331204 case item {
11341205 Field(_, state, _) -> [state, ..acc]