elm-pages 3.0.0-beta.4 → 3.0.0-beta.40

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (140) hide show
  1. package/README.md +10 -1
  2. package/adapter/netlify.js +207 -0
  3. package/codegen/{elm-pages-codegen.js → elm-pages-codegen.cjs} +2678 -2725
  4. package/generator/dead-code-review/elm-stuff/tests-0.19.1/elm-stuff/0.19.1/Pages-Review-DeadCodeEliminateData.elmi +0 -0
  5. package/generator/dead-code-review/elm-stuff/tests-0.19.1/elm-stuff/0.19.1/Pages-Review-DeadCodeEliminateData.elmo +0 -0
  6. package/generator/dead-code-review/elm-stuff/tests-0.19.1/elm-stuff/0.19.1/Pages-Review-DeadCodeEliminateDataTest.elmo +0 -0
  7. package/generator/dead-code-review/elm-stuff/tests-0.19.1/elm-stuff/0.19.1/d.dat +0 -0
  8. package/generator/dead-code-review/elm-stuff/tests-0.19.1/elm-stuff/0.19.1/i.dat +0 -0
  9. package/generator/dead-code-review/elm-stuff/tests-0.19.1/elm-stuff/0.19.1/o.dat +0 -0
  10. package/generator/dead-code-review/elm-stuff/tests-0.19.1/elm.json +1 -1
  11. package/generator/dead-code-review/elm-stuff/tests-0.19.1/js/Reporter.elm.js +1447 -342
  12. package/generator/dead-code-review/elm-stuff/tests-0.19.1/js/Runner.elm.js +17004 -13817
  13. package/generator/dead-code-review/elm-stuff/tests-0.19.1/js/node_runner.js +1 -1
  14. package/generator/dead-code-review/elm-stuff/tests-0.19.1/js/node_supervisor.js +4 -4
  15. package/generator/dead-code-review/elm.json +9 -7
  16. package/generator/dead-code-review/src/Pages/Review/DeadCodeEliminateData.elm +59 -10
  17. package/generator/dead-code-review/tests/Pages/Review/DeadCodeEliminateDataTest.elm +52 -36
  18. package/generator/review/elm-stuff/tests-0.19.1/elm-stuff/0.19.1/Pages-Internal-RoutePattern.elmi +0 -0
  19. package/generator/review/elm-stuff/tests-0.19.1/elm-stuff/0.19.1/Pages-Internal-RoutePattern.elmo +0 -0
  20. package/generator/review/elm-stuff/tests-0.19.1/elm-stuff/0.19.1/Pages-Review-NoContractViolations.elmi +0 -0
  21. package/generator/review/elm-stuff/tests-0.19.1/elm-stuff/0.19.1/Pages-Review-NoContractViolations.elmo +0 -0
  22. package/generator/review/elm-stuff/tests-0.19.1/elm-stuff/0.19.1/d.dat +0 -0
  23. package/generator/review/elm-stuff/tests-0.19.1/elm-stuff/0.19.1/i.dat +0 -0
  24. package/generator/review/elm-stuff/tests-0.19.1/elm-stuff/0.19.1/o.dat +0 -0
  25. package/generator/review/elm-stuff/tests-0.19.1/elm.json +1 -1
  26. package/generator/review/elm-stuff/tests-0.19.1/js/Reporter.elm.js +1447 -342
  27. package/generator/review/elm-stuff/tests-0.19.1/js/Runner.elm.js +25025 -21739
  28. package/generator/review/elm-stuff/tests-0.19.1/js/node_runner.js +1 -1
  29. package/generator/review/elm-stuff/tests-0.19.1/js/node_supervisor.js +4 -4
  30. package/generator/review/elm.json +10 -10
  31. package/generator/src/RouteBuilder.elm +115 -109
  32. package/generator/src/SharedTemplate.elm +3 -2
  33. package/generator/src/SiteConfig.elm +3 -2
  34. package/generator/src/basepath-middleware.js +3 -3
  35. package/generator/src/build.js +209 -92
  36. package/generator/src/cli.js +292 -88
  37. package/generator/src/codegen.js +29 -27
  38. package/generator/src/compatibility-key.js +3 -0
  39. package/generator/src/compile-elm.js +43 -26
  40. package/generator/src/config.js +39 -0
  41. package/generator/src/copy-dir.js +2 -2
  42. package/generator/src/dev-server.js +176 -138
  43. package/generator/src/dir-helpers.js +9 -26
  44. package/generator/src/elm-codegen.js +5 -4
  45. package/generator/src/elm-file-constants.js +2 -3
  46. package/generator/src/error-formatter.js +12 -11
  47. package/generator/src/file-helpers.js +3 -4
  48. package/generator/src/generate-template-module-connector.js +23 -23
  49. package/generator/src/init.js +9 -8
  50. package/generator/src/pre-render-html.js +39 -28
  51. package/generator/src/render-test.js +109 -0
  52. package/generator/src/render-worker.js +25 -28
  53. package/generator/src/render.js +321 -142
  54. package/generator/src/request-cache.js +265 -162
  55. package/generator/src/resolve-elm-module.js +64 -0
  56. package/generator/src/rewrite-client-elm-json.js +6 -5
  57. package/generator/src/rewrite-elm-json-help.js +56 -0
  58. package/generator/src/rewrite-elm-json.js +17 -7
  59. package/generator/src/route-codegen-helpers.js +16 -31
  60. package/generator/src/seo-renderer.js +12 -7
  61. package/generator/src/vite-utils.js +77 -0
  62. package/generator/static-code/elm-pages.js +10 -0
  63. package/generator/static-code/hmr.js +79 -13
  64. package/generator/template/app/Api.elm +6 -5
  65. package/generator/template/app/Effect.elm +123 -0
  66. package/generator/template/app/ErrorPage.elm +37 -6
  67. package/generator/template/app/Route/Index.elm +17 -10
  68. package/generator/template/app/Shared.elm +24 -47
  69. package/generator/template/app/Site.elm +19 -6
  70. package/generator/template/app/View.elm +1 -8
  71. package/generator/template/elm-tooling.json +0 -3
  72. package/generator/template/elm.json +32 -24
  73. package/generator/template/package.json +10 -4
  74. package/package.json +30 -27
  75. package/src/ApiRoute.elm +199 -61
  76. package/src/BackendTask/Custom.elm +325 -0
  77. package/src/BackendTask/Env.elm +90 -0
  78. package/src/{DataSource → BackendTask}/File.elm +171 -56
  79. package/src/{DataSource → BackendTask}/Glob.elm +136 -125
  80. package/src/BackendTask/Http.elm +679 -0
  81. package/src/{DataSource → BackendTask}/Internal/Glob.elm +1 -1
  82. package/src/BackendTask/Internal/Request.elm +69 -0
  83. package/src/BackendTask/Random.elm +79 -0
  84. package/src/BackendTask/Time.elm +47 -0
  85. package/src/BackendTask.elm +537 -0
  86. package/src/FatalError.elm +90 -0
  87. package/src/Head.elm +237 -7
  88. package/src/HtmlPrinter.elm +7 -3
  89. package/src/Internal/ApiRoute.elm +7 -5
  90. package/src/PageServerResponse.elm +6 -1
  91. package/src/Pages/Form.elm +229 -0
  92. package/src/Pages/GeneratorProgramConfig.elm +15 -0
  93. package/src/Pages/Internal/FatalError.elm +5 -0
  94. package/src/Pages/Internal/Msg.elm +93 -0
  95. package/src/Pages/Internal/Platform/Cli.elm +612 -763
  96. package/src/Pages/Internal/Platform/CompatibilityKey.elm +6 -0
  97. package/src/Pages/Internal/Platform/Effect.elm +1 -2
  98. package/src/Pages/Internal/Platform/GeneratorApplication.elm +379 -0
  99. package/src/Pages/Internal/Platform/StaticResponses.elm +65 -276
  100. package/src/Pages/Internal/Platform/ToJsPayload.elm +6 -9
  101. package/src/Pages/Internal/Platform.elm +327 -194
  102. package/src/Pages/Internal/Script.elm +17 -0
  103. package/src/Pages/Internal/StaticHttpBody.elm +35 -1
  104. package/src/Pages/Manifest.elm +29 -4
  105. package/src/Pages/PageUrl.elm +23 -9
  106. package/src/Pages/ProgramConfig.elm +26 -15
  107. package/src/Pages/Script.elm +109 -0
  108. package/src/Pages/SiteConfig.elm +3 -2
  109. package/src/Pages/StaticHttp/Request.elm +2 -2
  110. package/src/Pages/StaticHttpRequest.elm +23 -99
  111. package/src/Pages/Transition.elm +12 -3
  112. package/src/PagesMsg.elm +82 -0
  113. package/src/Path.elm +16 -19
  114. package/src/QueryParams.elm +21 -172
  115. package/src/RequestsAndPending.elm +37 -20
  116. package/src/Result/Extra.elm +26 -0
  117. package/src/Scaffold/Form.elm +546 -0
  118. package/src/Scaffold/Route.elm +1402 -0
  119. package/src/Server/Request.elm +73 -72
  120. package/src/Server/Session.elm +62 -42
  121. package/src/Server/SetCookie.elm +12 -4
  122. package/src/Stub.elm +53 -0
  123. package/src/Test/Html/Internal/ElmHtml/ToString.elm +8 -9
  124. package/src/DataSource/Env.elm +0 -38
  125. package/src/DataSource/Http.elm +0 -446
  126. package/src/DataSource/Internal/Request.elm +0 -20
  127. package/src/DataSource/Port.elm +0 -90
  128. package/src/DataSource.elm +0 -538
  129. package/src/Form/Field.elm +0 -717
  130. package/src/Form/FieldStatus.elm +0 -36
  131. package/src/Form/FieldView.elm +0 -417
  132. package/src/Form/FormData.elm +0 -22
  133. package/src/Form/Validation.elm +0 -391
  134. package/src/Form/Value.elm +0 -118
  135. package/src/Form.elm +0 -1683
  136. package/src/FormDecoder.elm +0 -102
  137. package/src/Pages/FormState.elm +0 -256
  138. package/src/Pages/Generate.elm +0 -800
  139. package/src/Pages/Internal/Form.elm +0 -17
  140. package/src/Pages/Msg.elm +0 -79
package/src/Form.elm DELETED
@@ -1,1683 +0,0 @@
1
- module Form exposing
2
- ( Form, HtmlForm, StyledHtmlForm, DoneForm
3
- , Response(..)
4
- , init
5
- , field, hiddenField, hiddenKind
6
- , Context
7
- , renderHtml, renderStyledHtml
8
- , FinalForm, withGetMethod, toDynamicTransition, toDynamicFetcher
9
- , Errors, errorsForField
10
- , parse, runServerSide, runOneOfServerSide
11
- , ServerForms(..)
12
- , initCombined, combine, initCombinedServer, combineServer
13
- , dynamic
14
- , AppContext
15
- , toServerForm, withOnSubmit
16
- -- subGroup
17
- )
18
-
19
- {-| One of the core features of elm-pages is helping you manage form data end-to-end, including
20
-
21
- - Presenting the HTML form with its fields
22
- - Maintaining client-side form state
23
- - Showing validation errors on the client-side
24
- - Receiving a form submission on the server-side
25
- - Using the exact same client-side validations on the server-side
26
- - Letting you run server-only Validations with DataSource's (things like checking for a unique username)
27
-
28
- Because elm-pages is a framework, it has its own internal Model and Msg's. That means you, the user,
29
- can offload some of the responsibility to elm-pages and build an interactive form with real-time
30
- client-side state and validation errors without wiring up your own Model and Msg's to manage that
31
- state. You define the source of truth for your form (how to parse it into data or errors), and
32
- elm-pages manages the state.
33
-
34
- Let's look at a sign-up form example.
35
-
36
-
37
- ### Step 1 - Define the Form
38
-
39
- What to look for:
40
-
41
- **The field declarations**
42
-
43
- Below the `Form.init` call you will find all of the form's fields declared with
44
-
45
- |> Form.field ...
46
-
47
- These are the form's field declarations.
48
-
49
- These fields each have individual validations. For example, `|> Field.required ...` means we'll get a validation
50
- error if that field is empty (similar for checking the minimum password length).
51
-
52
- There will be a corresponding parameter in the function we pass in to `Form.init` for every
53
- field declaration (in this example, `\email password passwordConfirmation -> ...`).
54
-
55
- **The `combine` validation**
56
-
57
- In addition to the validation errors that individual fields can have independently (like
58
- required fields or minimum password length), we can also do _dependent validations_.
59
-
60
- We use the [`Form.Validation`](Form-Validation) module to take each individual field and combine
61
- them into a type and/or errors.
62
-
63
- **The `view`**
64
-
65
- Totally customizable. Uses [`Form.FieldView`](Form-FieldView) to render all of the fields declared.
66
-
67
- import DataSource exposing (DataSource)
68
- import ErrorPage exposing (ErrorPage)
69
- import Form
70
- import Form.Field as Field
71
- import Form.FieldView as FieldView
72
- import Form.Validation as Validation
73
- import Html exposing (Html)
74
- import Html.Attributes as Attr
75
- import Route
76
- import Server.Request as Request
77
- import Server.Response exposing (Response)
78
-
79
- type alias NewUser =
80
- { email : String
81
- , password : String
82
- }
83
-
84
- signupForm : Form.HtmlForm String NewUser () Msg
85
- signupForm =
86
- Form.init
87
- (\email password passwordConfirmation ->
88
- { combine =
89
- Validation.succeed Login
90
- |> Validation.andMap email
91
- |> Validation.andMap
92
- (Validation.map2
93
- (\pass confirmation ->
94
- if pass == confirmation then
95
- Validation.succeed pass
96
-
97
- else
98
- passwordConfirmation
99
- |> Validation.fail
100
- "Must match password"
101
- )
102
- password
103
- passwordConfirmation
104
- |> Validation.andThen identity
105
- )
106
- , view =
107
- \info ->
108
- [ Html.label []
109
- [ fieldView info "Email" email
110
- , fieldView info "Password" password
111
- , fieldView info "Confirm Password" passwordConfirmation
112
- ]
113
- , Html.button []
114
- [ if info.isTransitioning then
115
- Html.text "Signing Up..."
116
-
117
- else
118
- Html.text "Sign Up"
119
- ]
120
- ]
121
- }
122
- )
123
- |> Form.field "email"
124
- (Field.text
125
- |> Field.required "Required"
126
- )
127
- |> Form.field "password"
128
- passwordField
129
- |> Form.field "passwordConfirmation"
130
- passwordField
131
-
132
- passwordField =
133
- Field.text
134
- |> Field.password
135
- |> Field.required "Required"
136
- |> Field.withClientValidation
137
- (\password ->
138
- ( Just password
139
- , if String.length password < 4 then
140
- [ "Must be at least 4 characters" ]
141
-
142
- else
143
- []
144
- )
145
- )
146
-
147
- fieldView :
148
- Form.Context String data
149
- -> String
150
- -> Validation.Field String parsed FieldView.Input
151
- -> Html msg
152
- fieldView formState label field =
153
- Html.div []
154
- [ Html.label []
155
- [ Html.text (label ++ " ")
156
- , field |> Form.FieldView.input []
157
- ]
158
- , (if formState.submitAttempted then
159
- formState.errors
160
- |> Form.errorsForField field
161
- |> List.map
162
- (\error ->
163
- Html.li [] [ Html.text error ]
164
- )
165
-
166
- else
167
- []
168
- )
169
- |> Html.ul [ Attr.style "color" "red" ]
170
- ]
171
-
172
-
173
- ### Step 2 - Render the Form's View
174
-
175
- view maybeUrl sharedModel app =
176
- { title = "Sign Up"
177
- , body =
178
- [ form
179
- |> Form.toDynamicTransition "login"
180
- |> Form.renderHtml [] Nothing app ()
181
- ]
182
- }
183
-
184
-
185
- ### Step 3 - Handle Server-Side Form Submissions
186
-
187
- action : RouteParams -> Request.Parser (DataSource (Response ActionData ErrorPage))
188
- action routeParams =
189
- Request.formData [ signupForm ]
190
- |> Request.map
191
- (\signupResult ->
192
- case signupResult of
193
- Ok newUser ->
194
- newUser
195
- |> myCreateUserDataSource
196
- |> DataSource.map
197
- (\() ->
198
- -- redirect to the home page
199
- -- after successful sign-up
200
- Route.redirectTo Route.Index
201
- )
202
-
203
- Err _ ->
204
- Route.redirectTo Route.Login
205
- |> DataSource.succeed
206
- )
207
-
208
- myCreateUserDataSource : DataSource ()
209
- myCreateUserDataSource =
210
- DataSource.fail
211
- "TODO - make a database call to create a new user"
212
-
213
-
214
- ## Building a Form Parser
215
-
216
- @docs Form, HtmlForm, StyledHtmlForm, DoneForm
217
-
218
- @docs Response
219
-
220
- @docs init
221
-
222
-
223
- ## Adding Fields
224
-
225
- @docs field, hiddenField, hiddenKind
226
-
227
-
228
- ## View Functions
229
-
230
- @docs Context
231
-
232
-
233
- ## Rendering Forms
234
-
235
- @docs renderHtml, renderStyledHtml
236
-
237
- @docs FinalForm, withGetMethod, toDynamicTransition, toDynamicFetcher
238
-
239
-
240
- ## Showing Errors
241
-
242
- @docs Errors, errorsForField
243
-
244
-
245
- ## Running Parsers
246
-
247
- @docs parse, runServerSide, runOneOfServerSide
248
-
249
-
250
- ## Combining Forms to Run on Server
251
-
252
- @docs ServerForms
253
-
254
- @docs initCombined, combine, initCombinedServer, combineServer
255
-
256
-
257
- ## Dynamic Fields
258
-
259
- @docs dynamic
260
-
261
- @docs AppContext
262
-
263
-
264
- ## Submission
265
-
266
- @docs toServerForm, withOnSubmit
267
-
268
- -}
269
-
270
- import DataSource exposing (DataSource)
271
- import Dict exposing (Dict)
272
- import Form.Field as Field exposing (Field(..))
273
- import Form.FieldStatus as FieldStatus exposing (FieldStatus)
274
- import Form.FieldView
275
- import Form.Validation as Validation exposing (Combined, Validation)
276
- import Html exposing (Html)
277
- import Html.Attributes as Attr
278
- import Html.Lazy
279
- import Html.Styled
280
- import Html.Styled.Attributes as StyledAttr
281
- import Html.Styled.Lazy
282
- import Pages.FormState as Form exposing (FormState)
283
- import Pages.Internal.Form exposing (Validation(..))
284
- import Pages.Msg
285
- import Pages.Transition exposing (Transition(..))
286
- import Path exposing (Path)
287
-
288
-
289
-
290
- --{-| -}
291
- --type
292
- -- ParseResult error decoded
293
- -- -- TODO parse into both errors AND a decoded value
294
- -- = Success decoded
295
- -- | DecodedWithErrors (Dict String (List error)) decoded
296
- -- | DecodeFailure (Dict String (List error))
297
-
298
-
299
- {-| -}
300
- initFormState : FormState
301
- initFormState =
302
- { fields = Dict.empty
303
- , submitAttempted = False
304
- }
305
-
306
-
307
- {-| -}
308
- type alias Context error data =
309
- { errors : Errors error
310
- , isTransitioning : Bool
311
- , submitAttempted : Bool
312
- , data : data
313
- }
314
-
315
-
316
- {-| -}
317
- init : combineAndView -> Form String combineAndView data
318
- init combineAndView =
319
- Form []
320
- (\_ _ ->
321
- { result = Dict.empty
322
- , combineAndView = combineAndView
323
- , isMatchCandidate = True
324
- }
325
- )
326
- (\_ -> [])
327
-
328
-
329
- {-| -}
330
- dynamic :
331
- (decider
332
- ->
333
- Form
334
- error
335
- { combine : Validation error parsed named constraints1
336
- , view : subView
337
- }
338
- data
339
- )
340
- ->
341
- Form
342
- error
343
- --((decider -> Validation error parsed named) -> combined)
344
- ({ combine : decider -> Validation error parsed named constraints1
345
- , view : decider -> subView
346
- }
347
- -> combineAndView
348
- )
349
- data
350
- ->
351
- Form
352
- error
353
- combineAndView
354
- data
355
- dynamic forms formBuilder =
356
- Form []
357
- (\maybeData formState ->
358
- let
359
- toParser :
360
- decider
361
- ->
362
- { result : Dict String (List error)
363
- , isMatchCandidate : Bool
364
- , combineAndView : { combine : Validation error parsed named constraints1, view : subView }
365
- }
366
- toParser decider =
367
- case forms decider of
368
- Form _ parseFn _ ->
369
- -- TODO need to include hidden form fields from `definitions` (should they be automatically rendered? Does that mean the view type needs to be hardcoded?)
370
- parseFn maybeData formState
371
-
372
- myFn :
373
- { result : Dict String (List error)
374
- , isMatchCandidate : Bool
375
- , combineAndView : combineAndView
376
- }
377
- myFn =
378
- let
379
- newThing :
380
- { result : Dict String (List error)
381
- , isMatchCandidate : Bool
382
- , combineAndView : { combine : decider -> Validation error parsed named constraints1, view : decider -> subView } -> combineAndView
383
- }
384
- newThing =
385
- case formBuilder of
386
- Form _ parseFn _ ->
387
- parseFn maybeData formState
388
-
389
- arg : { combine : decider -> Validation error parsed named constraints1, view : decider -> subView }
390
- arg =
391
- { combine =
392
- toParser
393
- >> .combineAndView
394
- >> .combine
395
- , view =
396
- \decider ->
397
- decider
398
- |> toParser
399
- |> .combineAndView
400
- |> .view
401
- }
402
- in
403
- { result =
404
- newThing.result
405
- , combineAndView =
406
- newThing.combineAndView arg
407
- , isMatchCandidate = newThing.isMatchCandidate
408
- }
409
- in
410
- myFn
411
- )
412
- (\_ -> [])
413
-
414
-
415
-
416
- --{-| -}
417
- --subGroup :
418
- -- Form error ( Maybe parsed, Dict String (List error) ) data (Context error data -> subView)
419
- -- ->
420
- -- Form
421
- -- error
422
- -- ({ value : parsed } -> combined)
423
- -- data
424
- -- (Context error data -> (subView -> combinedView))
425
- -- -> Form error combined data (Context error data -> combinedView)
426
- --subGroup forms formBuilder =
427
- -- Form []
428
- -- (\maybeData formState ->
429
- -- let
430
- -- toParser : { result : ( Maybe ( Maybe parsed, Dict String (List error) ), Dict String (List error) ), view : Context error data -> subView }
431
- -- toParser =
432
- -- case forms of
433
- -- Form definitions parseFn toInitialValues ->
434
- -- -- TODO need to include hidden form fields from `definitions` (should they be automatically rendered? Does that mean the view type needs to be hardcoded?)
435
- -- parseFn maybeData formState
436
- --
437
- -- myFn :
438
- -- { result : ( Maybe combined, Dict String (List error) )
439
- -- , view : Context error data -> combinedView
440
- -- }
441
- -- myFn =
442
- -- let
443
- -- deciderToParsed : ( Maybe parsed, Dict String (List error) )
444
- -- deciderToParsed =
445
- -- toParser |> mergeResults
446
- --
447
- -- newThing : { result : ( Maybe ({ value : parsed } -> combined), Dict String (List error) ), view : Context error data -> subView -> combinedView }
448
- -- newThing =
449
- -- case formBuilder of
450
- -- Form definitions parseFn toInitialValues ->
451
- -- parseFn maybeData formState
452
- --
453
- -- anotherThing : Maybe combined
454
- -- anotherThing =
455
- -- Maybe.map2
456
- -- (\runFn parsed ->
457
- -- runFn { value = parsed }
458
- -- )
459
- -- (Tuple.first newThing.result)
460
- -- (deciderToParsed |> Tuple.first)
461
- -- in
462
- -- { result =
463
- -- ( anotherThing
464
- -- , mergeErrors (newThing.result |> Tuple.second)
465
- -- (deciderToParsed |> Tuple.second)
466
- -- )
467
- -- , view =
468
- -- \fieldErrors ->
469
- -- let
470
- -- something2 : subView
471
- -- something2 =
472
- -- fieldErrors
473
- -- |> (toParser
474
- -- |> .view
475
- -- )
476
- -- in
477
- -- newThing.view fieldErrors something2
478
- -- }
479
- -- in
480
- -- myFn
481
- -- )
482
- -- (\_ -> [])
483
-
484
-
485
- {-| Declare a visible field for the form.
486
-
487
- Use [`Form.Field`](Form-Field) to define the field and its validations.
488
-
489
- form =
490
- Form.init
491
- (\email ->
492
- { combine =
493
- Validation.succeed NewUser
494
- |> Validation.andMap email
495
- , view = \info -> [{- render fields -}]
496
- }
497
- )
498
- |> Form.field "email"
499
- (Field.text |> Field.required "Required")
500
-
501
- -}
502
- field :
503
- String
504
- -> Field error parsed data kind constraints
505
- -> Form error (Validation.Field error parsed kind -> combineAndView) data
506
- -> Form error combineAndView data
507
- field name (Field fieldParser kind) (Form definitions parseFn toInitialValues) =
508
- Form
509
- (( name, RegularField )
510
- :: definitions
511
- )
512
- (\maybeData formState ->
513
- let
514
- ( maybeParsed, errors ) =
515
- -- @@@@@@ use code from here
516
- fieldParser.decode rawFieldValue
517
-
518
- ( rawFieldValue, fieldStatus ) =
519
- case formState.fields |> Dict.get name of
520
- Just info ->
521
- ( Just info.value, info.status )
522
-
523
- Nothing ->
524
- ( Maybe.map2 (|>) maybeData fieldParser.initialValue, FieldStatus.NotVisited )
525
-
526
- thing : Pages.Internal.Form.ViewField kind
527
- thing =
528
- { value = rawFieldValue
529
- , status = fieldStatus
530
- , kind = ( kind, fieldParser.properties )
531
- }
532
-
533
- parsedField : Validation.Field error parsed kind
534
- parsedField =
535
- Pages.Internal.Form.Validation (Just thing) (Just name) ( maybeParsed, Dict.empty )
536
-
537
- myFn :
538
- { result : Dict String (List error)
539
- , combineAndView : Validation.Field error parsed kind -> combineAndView
540
- , isMatchCandidate : Bool
541
- }
542
- ->
543
- { result : Dict String (List error)
544
- , combineAndView : combineAndView
545
- , isMatchCandidate : Bool
546
- }
547
- myFn soFar =
548
- let
549
- validationField : Validation.Field error parsed kind
550
- validationField =
551
- parsedField
552
- in
553
- { result =
554
- soFar.result
555
- |> addErrorsInternal name errors
556
- , combineAndView =
557
- soFar.combineAndView validationField
558
- , isMatchCandidate = soFar.isMatchCandidate
559
- }
560
- in
561
- formState
562
- |> parseFn maybeData
563
- |> myFn
564
- )
565
- (\data ->
566
- case fieldParser.initialValue of
567
- Just toInitialValue ->
568
- ( name, toInitialValue data )
569
- :: toInitialValues data
570
-
571
- Nothing ->
572
- toInitialValues data
573
- )
574
-
575
-
576
- {-| Declare a hidden field for the form.
577
-
578
- Unlike [`field`](#field) declarations which are rendered using [`Form.FieldView`](Form-FieldView)
579
- functions, `hiddenField` inputs are automatically inserted into the form when you render it.
580
-
581
- You define the field's validations the same way as for `field`, with the
582
- [`Form.Field`](Form-Field) API.
583
-
584
- form =
585
- Form.init
586
- (\quantity productId ->
587
- { combine = {- combine fields -}
588
- , view = \info -> [{- render visible fields -}]
589
- }
590
- )
591
- |> Form.field "quantity"
592
- (Field.int |> Field.required "Required")
593
- |> Form.field "productId"
594
- (Field.text
595
- |> Field.required "Required"
596
- |> Field.withInitialValue (\product -> Form.Value.string product.id)
597
- )
598
-
599
- -}
600
- hiddenField :
601
- String
602
- -> Field error parsed data kind constraints
603
- -> Form error (Validation.Field error parsed Form.FieldView.Hidden -> combineAndView) data
604
- -> Form error combineAndView data
605
- hiddenField name (Field fieldParser _) (Form definitions parseFn toInitialValues) =
606
- Form
607
- (( name, HiddenField )
608
- :: definitions
609
- )
610
- (\maybeData formState ->
611
- let
612
- ( maybeParsed, errors ) =
613
- fieldParser.decode rawFieldValue
614
-
615
- ( rawFieldValue, fieldStatus ) =
616
- case formState.fields |> Dict.get name of
617
- Just info ->
618
- ( Just info.value, info.status )
619
-
620
- Nothing ->
621
- ( Maybe.map2 (|>) maybeData fieldParser.initialValue, FieldStatus.NotVisited )
622
-
623
- thing : Pages.Internal.Form.ViewField Form.FieldView.Hidden
624
- thing =
625
- { value = rawFieldValue
626
- , status = fieldStatus
627
- , kind = ( Form.FieldView.Hidden, fieldParser.properties )
628
- }
629
-
630
- parsedField : Validation.Field error parsed Form.FieldView.Hidden
631
- parsedField =
632
- Pages.Internal.Form.Validation (Just thing) (Just name) ( maybeParsed, Dict.empty )
633
-
634
- myFn :
635
- { result : Dict String (List error)
636
- , combineAndView : Validation.Field error parsed Form.FieldView.Hidden -> combineAndView
637
- , isMatchCandidate : Bool
638
- }
639
- ->
640
- { result : Dict String (List error)
641
- , combineAndView : combineAndView
642
- , isMatchCandidate : Bool
643
- }
644
- myFn soFar =
645
- let
646
- validationField : Validation.Field error parsed Form.FieldView.Hidden
647
- validationField =
648
- parsedField
649
- in
650
- { result =
651
- soFar.result
652
- |> addErrorsInternal name errors
653
- , combineAndView =
654
- soFar.combineAndView validationField
655
- , isMatchCandidate = soFar.isMatchCandidate
656
- }
657
- in
658
- formState
659
- |> parseFn maybeData
660
- |> myFn
661
- )
662
- (\data ->
663
- case fieldParser.initialValue of
664
- Just toInitialValue ->
665
- ( name, toInitialValue data )
666
- :: toInitialValues data
667
-
668
- Nothing ->
669
- toInitialValues data
670
- )
671
-
672
-
673
- {-| -}
674
- toServerForm :
675
- Form
676
- error
677
- { combine : Validation error combined kind constraints
678
- , view : viewFn
679
- }
680
- data
681
- ->
682
- Form
683
- error
684
- { combine : Validation error (DataSource (Validation error combined kind constraints)) kind constraints
685
- , view : viewFn
686
- }
687
- data
688
- toServerForm (Form a b c) =
689
- let
690
- mappedB :
691
- Maybe data
692
- -> FormState
693
- ->
694
- { result : Dict String (List error)
695
- , isMatchCandidate : Bool
696
- , combineAndView :
697
- { combine : Validation error (DataSource (Validation error combined kind constraints)) kind constraints
698
- , view : viewFn
699
- }
700
- }
701
- mappedB maybeData formState =
702
- b maybeData formState
703
- |> (\thing ->
704
- { result = thing.result
705
- , combineAndView =
706
- { combine =
707
- thing.combineAndView.combine
708
- |> DataSource.succeed
709
- |> Validation.succeed2
710
- , view = thing.combineAndView.view
711
- }
712
- , isMatchCandidate = thing.isMatchCandidate
713
- }
714
- )
715
- in
716
- Form a mappedB c
717
-
718
-
719
- {-| -}
720
- hiddenKind :
721
- ( String, String )
722
- -> error
723
- -> Form error combineAndView data
724
- -> Form error combineAndView data
725
- hiddenKind ( name, value ) error_ (Form definitions parseFn toInitialValues) =
726
- let
727
- (Field fieldParser _) =
728
- Field.exactValue value error_
729
- in
730
- Form
731
- (( name, HiddenField )
732
- :: definitions
733
- )
734
- (\maybeData formState ->
735
- let
736
- ( decodedValue, errors ) =
737
- fieldParser.decode rawFieldValue
738
-
739
- rawFieldValue : Maybe String
740
- rawFieldValue =
741
- case formState.fields |> Dict.get name of
742
- Just info ->
743
- Just info.value
744
-
745
- Nothing ->
746
- Maybe.map2 (|>) maybeData fieldParser.initialValue
747
-
748
- myFn :
749
- { result : Dict String (List error)
750
- , isMatchCandidate : Bool
751
- , combineAndView : combineAndView
752
- }
753
- ->
754
- { result : Dict String (List error)
755
- , isMatchCandidate : Bool
756
- , combineAndView : combineAndView
757
- }
758
- myFn soFar =
759
- { result =
760
- soFar.result
761
- |> addErrorsInternal name errors
762
- , combineAndView = soFar.combineAndView
763
- , isMatchCandidate = soFar.isMatchCandidate && decodedValue == Just value
764
- }
765
- in
766
- formState
767
- |> parseFn maybeData
768
- |> myFn
769
- )
770
- (\data ->
771
- case fieldParser.initialValue of
772
- Just toInitialValue ->
773
- ( name, toInitialValue data )
774
- :: toInitialValues data
775
-
776
- Nothing ->
777
- toInitialValues data
778
- )
779
-
780
-
781
- {-| -}
782
- type Errors error
783
- = Errors (Dict String (List error))
784
-
785
-
786
- {-| -}
787
- errorsForField : Validation.Field error parsed kind -> Errors error -> List error
788
- errorsForField field_ (Errors errorsDict) =
789
- errorsDict
790
- |> Dict.get (Validation.fieldName field_)
791
- |> Maybe.withDefault []
792
-
793
-
794
- {-| -}
795
- type alias AppContext app actionData =
796
- { app
797
- | --, sharedData : Shared.Data
798
- --, routeParams : routeParams
799
- path : Path
800
-
801
- --, action : Maybe action
802
- --, submit :
803
- -- { fields : List ( String, String ), headers : List ( String, String ) }
804
- -- -> Pages.Fetcher.Fetcher (Result Http.Error action)
805
- , transition : Maybe Transition
806
- , fetchers : Dict String (Pages.Transition.FetcherState actionData)
807
- , pageFormState :
808
- Dict String { fields : Dict String { value : String, status : FieldStatus }, submitAttempted : Bool }
809
- }
810
-
811
-
812
- mergeResults :
813
- { a | result : ( Validation error parsed named constraints1, Dict String (List error) ) }
814
- -> Validation error parsed unnamed constraints2
815
- mergeResults parsed =
816
- case parsed.result of
817
- ( Pages.Internal.Form.Validation _ name ( parsedThing, combineErrors ), individualFieldErrors ) ->
818
- Pages.Internal.Form.Validation Nothing
819
- name
820
- ( parsedThing
821
- , mergeErrors combineErrors individualFieldErrors
822
- )
823
-
824
-
825
- mergeErrors : Dict comparable (List value) -> Dict comparable (List value) -> Dict comparable (List value)
826
- mergeErrors errors1 errors2 =
827
- Dict.merge
828
- (\key entries soFar ->
829
- soFar |> insertIfNonempty key entries
830
- )
831
- (\key entries1 entries2 soFar ->
832
- soFar |> insertIfNonempty key (entries1 ++ entries2)
833
- )
834
- (\key entries soFar ->
835
- soFar |> insertIfNonempty key entries
836
- )
837
- errors1
838
- errors2
839
- Dict.empty
840
-
841
-
842
- {-| -}
843
- parse :
844
- String
845
- -> AppContext app actionData
846
- -> data
847
- -> Form error { info | combine : Validation error parsed named constraints } data
848
- -> ( Maybe parsed, Dict String (List error) )
849
- parse formId app data (Form _ parser _) =
850
- -- TODO Get transition context from `app` so you can check if the current form is being submitted
851
- -- TODO either as a transition or a fetcher? Should be easy enough to check for the `id` on either of those?
852
- let
853
- parsed :
854
- { result : Dict String (List error)
855
- , isMatchCandidate : Bool
856
- , combineAndView : { info | combine : Validation error parsed named constraints }
857
- }
858
- parsed =
859
- parser (Just data) thisFormState
860
-
861
- thisFormState : FormState
862
- thisFormState =
863
- app.pageFormState
864
- |> Dict.get formId
865
- |> Maybe.withDefault initFormState
866
- in
867
- { result = ( parsed.combineAndView.combine, parsed.result )
868
- }
869
- |> mergeResults
870
- |> unwrapValidation
871
-
872
-
873
- insertIfNonempty : comparable -> List value -> Dict comparable (List value) -> Dict comparable (List value)
874
- insertIfNonempty key values dict =
875
- if values |> List.isEmpty then
876
- dict
877
-
878
- else
879
- dict
880
- |> Dict.insert key values
881
-
882
-
883
- {-| -}
884
- runServerSide :
885
- List ( String, String )
886
- -> Form error (Validation error parsed kind constraints) data
887
- -> ( Bool, ( Maybe parsed, Dict String (List error) ) )
888
- runServerSide rawFormData (Form _ parser _) =
889
- let
890
- parsed :
891
- { result : Dict String (List error)
892
- , isMatchCandidate : Bool
893
- , combineAndView : Validation error parsed kind constraints
894
- }
895
- parsed =
896
- parser Nothing thisFormState
897
-
898
- thisFormState : FormState
899
- thisFormState =
900
- { initFormState
901
- | fields =
902
- rawFormData
903
- |> List.map
904
- (Tuple.mapSecond
905
- (\value ->
906
- { value = value
907
- , status = FieldStatus.NotVisited
908
- }
909
- )
910
- )
911
- |> Dict.fromList
912
- }
913
- in
914
- ( parsed.isMatchCandidate
915
- , { result = ( parsed.combineAndView, parsed.result )
916
- }
917
- |> mergeResults
918
- |> unwrapValidation
919
- )
920
-
921
-
922
- unwrapValidation : Validation error parsed named constraints -> ( Maybe parsed, Dict String (List error) )
923
- unwrapValidation (Pages.Internal.Form.Validation _ _ ( maybeParsed, errors )) =
924
- ( maybeParsed, errors )
925
-
926
-
927
- {-| -}
928
- runOneOfServerSide :
929
- List ( String, String )
930
- -> ServerForms error parsed
931
- -> ( Maybe parsed, Dict String (List error) )
932
- runOneOfServerSide rawFormData forms =
933
- runOneOfServerSideHelp rawFormData Nothing forms
934
-
935
-
936
- {-| -}
937
- runOneOfServerSideHelp :
938
- List ( String, String )
939
- -> Maybe (List ( String, List error ))
940
- -> ServerForms error parsed
941
- -> ( Maybe parsed, Dict String (List error) )
942
- runOneOfServerSideHelp rawFormData firstFoundErrors (ServerForms parsers) =
943
- case parsers of
944
- firstParser :: remainingParsers ->
945
- let
946
- ( isMatchCandidate, thing1 ) =
947
- runServerSide rawFormData firstParser
948
-
949
- thing : ( Maybe parsed, List ( String, List error ) )
950
- thing =
951
- thing1
952
- |> Tuple.mapSecond
953
- (\errors ->
954
- errors
955
- |> Dict.toList
956
- |> List.filter (Tuple.second >> List.isEmpty >> not)
957
- )
958
- in
959
- case ( isMatchCandidate, thing ) of
960
- ( True, ( Just parsed, errors ) ) ->
961
- ( Just parsed, errors |> Dict.fromList )
962
-
963
- ( _, ( _, errors ) ) ->
964
- runOneOfServerSideHelp rawFormData
965
- (firstFoundErrors
966
- -- TODO is this logic what we want here? Might need to think through the semantics a bit more
967
- -- of which errors to parse into - could be the first errors, the last, or some other way of
968
- -- having higher precedence for deciding which form should be used
969
- |> Maybe.withDefault errors
970
- |> Just
971
- )
972
- (ServerForms remainingParsers)
973
-
974
- [] ->
975
- -- TODO need to pass errors
976
- ( Nothing, firstFoundErrors |> Maybe.withDefault [] |> Dict.fromList )
977
-
978
-
979
- {-| -}
980
- renderHtml :
981
- List (Html.Attribute (Pages.Msg.Msg msg))
982
- ->
983
- Maybe
984
- { fields : List ( String, String )
985
- , errors : Dict String (List error)
986
- }
987
- -> AppContext app actionData
988
- -> data
989
- ->
990
- FinalForm
991
- error
992
- (Validation error parsed named constraints)
993
- data
994
- (Context error data
995
- -> List (Html (Pages.Msg.Msg msg))
996
- )
997
- msg
998
- -> Html (Pages.Msg.Msg msg)
999
- renderHtml attrs maybe app data (FinalForm options a b c) =
1000
- Html.Lazy.lazy6 renderHelper attrs maybe options app data (FormInternal a b c)
1001
-
1002
-
1003
- {-| -}
1004
- type FinalForm error parsed data view userMsg
1005
- = FinalForm
1006
- (RenderOptions userMsg)
1007
- (List ( String, FieldDefinition ))
1008
- (Maybe data
1009
- -> FormState
1010
- ->
1011
- { result :
1012
- ( parsed
1013
- , Dict String (List error)
1014
- )
1015
- , isMatchCandidate : Bool
1016
- , view : view
1017
- }
1018
- )
1019
- (data -> List ( String, String ))
1020
-
1021
-
1022
- {-| -}
1023
- toDynamicFetcher :
1024
- String
1025
- ->
1026
- Form
1027
- error
1028
- { combine : Validation error parsed field constraints
1029
- , view : Context error data -> view
1030
- }
1031
- data
1032
- ->
1033
- FinalForm
1034
- error
1035
- (Validation error parsed field constraints)
1036
- data
1037
- (Context error data -> view)
1038
- userMsg
1039
- toDynamicFetcher name (Form a b c) =
1040
- let
1041
- options : { submitStrategy : SubmitStrategy, method : Method, name : Maybe String, onSubmit : Maybe a }
1042
- options =
1043
- { submitStrategy = FetcherStrategy
1044
- , method = Post
1045
- , name = Just name
1046
- , onSubmit = Nothing
1047
- }
1048
-
1049
- transformB :
1050
- (Maybe data
1051
- -> FormState
1052
- ->
1053
- { result : Dict String (List error)
1054
- , isMatchCandidate : Bool
1055
- , combineAndView :
1056
- { combine : Validation error parsed field constraints
1057
- , view : Context error data -> view
1058
- }
1059
- }
1060
- )
1061
- ->
1062
- (Maybe data
1063
- -> FormState
1064
- ->
1065
- { result :
1066
- ( Validation error parsed field constraints
1067
- , Dict String (List error)
1068
- )
1069
- , isMatchCandidate : Bool
1070
- , view : Context error data -> view
1071
- }
1072
- )
1073
- transformB rawB =
1074
- \maybeData formState ->
1075
- let
1076
- foo :
1077
- { result : Dict String (List error)
1078
- , isMatchCandidate : Bool
1079
- , combineAndView :
1080
- { combine : Validation error parsed field constraints
1081
- , view : Context error data -> view
1082
- }
1083
- }
1084
- foo =
1085
- rawB maybeData formState
1086
- in
1087
- { result = ( foo.combineAndView.combine, foo.result )
1088
- , view = foo.combineAndView.view
1089
- , isMatchCandidate = foo.isMatchCandidate
1090
- }
1091
- in
1092
- FinalForm options a (transformB b) c
1093
-
1094
-
1095
- {-| -}
1096
- toDynamicTransition :
1097
- String
1098
- ->
1099
- Form
1100
- error
1101
- { combine : Validation error parsed field constraints
1102
- , view : Context error data -> view
1103
- }
1104
- data
1105
- ->
1106
- FinalForm
1107
- error
1108
- (Validation error parsed field constraints)
1109
- data
1110
- (Context error data -> view)
1111
- userMsg
1112
- toDynamicTransition name (Form a b c) =
1113
- let
1114
- options : { submitStrategy : SubmitStrategy, method : Method, name : Maybe String, onSubmit : Maybe a }
1115
- options =
1116
- { submitStrategy = TransitionStrategy
1117
- , method = Post
1118
- , name = Just name
1119
- , onSubmit = Nothing
1120
- }
1121
-
1122
- transformB :
1123
- (Maybe data
1124
- -> FormState
1125
- ->
1126
- { result : Dict String (List error)
1127
- , isMatchCandidate : Bool
1128
- , combineAndView :
1129
- { combine : Validation error parsed field constraints
1130
- , view : Context error data -> view
1131
- }
1132
- }
1133
- )
1134
- ->
1135
- (Maybe data
1136
- -> FormState
1137
- ->
1138
- { result :
1139
- ( Validation error parsed field constraints
1140
- , Dict String (List error)
1141
- )
1142
- , isMatchCandidate : Bool
1143
- , view : Context error data -> view
1144
- }
1145
- )
1146
- transformB rawB =
1147
- \maybeData formState ->
1148
- let
1149
- foo :
1150
- { result : Dict String (List error)
1151
- , isMatchCandidate : Bool
1152
- , combineAndView :
1153
- { combine : Validation error parsed field constraints
1154
- , view : Context error data -> view
1155
- }
1156
- }
1157
- foo =
1158
- rawB maybeData formState
1159
- in
1160
- { result = ( foo.combineAndView.combine, foo.result )
1161
- , view = foo.combineAndView.view
1162
- , isMatchCandidate = foo.isMatchCandidate
1163
- }
1164
- in
1165
- FinalForm options a (transformB b) c
1166
-
1167
-
1168
- {-| -}
1169
- withGetMethod : FinalForm error parsed data view userMsg -> FinalForm error parsed data view userMsg
1170
- withGetMethod (FinalForm options a b c) =
1171
- FinalForm { options | method = Get } a b c
1172
-
1173
-
1174
- {-| -}
1175
- withOnSubmit : ({ fields : List ( String, String ) } -> userMsg) -> FinalForm error parsed data view userMsg -> FinalForm error parsed data view userMsg
1176
- withOnSubmit onSubmit (FinalForm options a b c) =
1177
- FinalForm { options | onSubmit = Just onSubmit } a b c
1178
-
1179
-
1180
- {-| -}
1181
- renderStyledHtml :
1182
- List (Html.Styled.Attribute (Pages.Msg.Msg msg))
1183
- ->
1184
- Maybe
1185
- { fields : List ( String, String )
1186
- , errors : Dict String (List error)
1187
- }
1188
- -> AppContext app actionData
1189
- -> data
1190
- ->
1191
- FinalForm
1192
- error
1193
- (Validation error parsed named constraints)
1194
- data
1195
- (Context error data
1196
- -> List (Html.Styled.Html (Pages.Msg.Msg msg))
1197
- )
1198
- msg
1199
- -> Html.Styled.Html (Pages.Msg.Msg msg)
1200
- renderStyledHtml attrs maybe app data (FinalForm options a b c) =
1201
- Html.Styled.Lazy.lazy6 renderStyledHelper attrs maybe options app data (FormInternal a b c)
1202
-
1203
-
1204
- {-| -}
1205
- type Response error
1206
- = Response
1207
- { fields : List ( String, String )
1208
- , errors : Dict String (List error)
1209
- }
1210
-
1211
-
1212
- renderHelper :
1213
- List (Html.Attribute (Pages.Msg.Msg msg))
1214
- ->
1215
- Maybe
1216
- { fields : List ( String, String )
1217
- , errors : Dict String (List error)
1218
- }
1219
- -> RenderOptions msg
1220
- -> AppContext app actionData
1221
- -> data
1222
- -> FormInternal error (Validation error parsed named constraints) data (Context error data -> List (Html (Pages.Msg.Msg msg)))
1223
- -> Html (Pages.Msg.Msg msg)
1224
- renderHelper attrs maybe options formState data form =
1225
- -- TODO Get transition context from `app` so you can check if the current form is being submitted
1226
- -- TODO either as a transition or a fetcher? Should be easy enough to check for the `id` on either of those?
1227
- let
1228
- { formId, hiddenInputs, children, isValid } =
1229
- helperValues toHiddenInput maybe options formState data form
1230
-
1231
- toHiddenInput : List (Html.Attribute (Pages.Msg.Msg msg)) -> Html (Pages.Msg.Msg msg)
1232
- toHiddenInput hiddenAttrs =
1233
- Html.input hiddenAttrs []
1234
- in
1235
- Html.form
1236
- (Form.listeners formId
1237
- ++ [ Attr.method (methodToString options.method)
1238
- , Attr.novalidate True
1239
-
1240
- -- TODO provide a way to override the action so users can submit to other Routes
1241
- , Attr.action (Path.toAbsolute formState.path)
1242
- , case options.submitStrategy of
1243
- FetcherStrategy ->
1244
- Pages.Msg.fetcherOnSubmit options.onSubmit formId (\_ -> isValid)
1245
-
1246
- TransitionStrategy ->
1247
- Pages.Msg.submitIfValid formId (\_ -> isValid)
1248
- ]
1249
- ++ attrs
1250
- )
1251
- (hiddenInputs ++ children)
1252
-
1253
-
1254
- renderStyledHelper :
1255
- List (Html.Styled.Attribute (Pages.Msg.Msg msg))
1256
- ->
1257
- Maybe
1258
- { fields : List ( String, String )
1259
- , errors : Dict String (List error)
1260
- }
1261
- -> RenderOptions msg
1262
- -> AppContext app actionData
1263
- -> data
1264
- -> FormInternal error (Validation error parsed named constraints) data (Context error data -> List (Html.Styled.Html (Pages.Msg.Msg msg)))
1265
- -> Html.Styled.Html (Pages.Msg.Msg msg)
1266
- renderStyledHelper attrs maybe options formState data form =
1267
- -- TODO Get transition context from `app` so you can check if the current form is being submitted
1268
- -- TODO either as a transition or a fetcher? Should be easy enough to check for the `id` on either of those?
1269
- let
1270
- { formId, hiddenInputs, children, isValid } =
1271
- helperValues toHiddenInput maybe options formState data form
1272
-
1273
- toHiddenInput : List (Html.Attribute (Pages.Msg.Msg msg)) -> Html.Styled.Html (Pages.Msg.Msg msg)
1274
- toHiddenInput hiddenAttrs =
1275
- Html.Styled.input (hiddenAttrs |> List.map StyledAttr.fromUnstyled) []
1276
- in
1277
- Html.Styled.form
1278
- ((Form.listeners formId |> List.map StyledAttr.fromUnstyled)
1279
- ++ [ StyledAttr.method (methodToString options.method)
1280
- , StyledAttr.novalidate True
1281
- , StyledAttr.action (Path.toAbsolute formState.path)
1282
- , case options.submitStrategy of
1283
- FetcherStrategy ->
1284
- StyledAttr.fromUnstyled <|
1285
- Pages.Msg.fetcherOnSubmit options.onSubmit formId (\_ -> isValid)
1286
-
1287
- TransitionStrategy ->
1288
- StyledAttr.fromUnstyled <|
1289
- Pages.Msg.submitIfValid formId (\_ -> isValid)
1290
- ]
1291
- ++ attrs
1292
- )
1293
- (hiddenInputs ++ children)
1294
-
1295
-
1296
- helperValues :
1297
- (List (Html.Attribute (Pages.Msg.Msg msg)) -> view)
1298
- ->
1299
- Maybe
1300
- { fields : List ( String, String )
1301
- , errors : Dict String (List error)
1302
- }
1303
- -> RenderOptions msg
1304
- -> AppContext app actionData
1305
- -> data
1306
- ---> Form error parsed data view
1307
- -> FormInternal error (Validation error parsed named constraints) data (Context error data -> List view)
1308
- -> { formId : String, hiddenInputs : List view, children : List view, isValid : Bool }
1309
- helperValues toHiddenInput maybe options formState data (FormInternal fieldDefinitions parser toInitialValues) =
1310
- let
1311
- formId : String
1312
- formId =
1313
- options.name |> Maybe.withDefault ""
1314
-
1315
- initialValues : Dict String Form.FieldState
1316
- initialValues =
1317
- toInitialValues data
1318
- |> List.map (Tuple.mapSecond (\value -> { value = value, status = FieldStatus.NotVisited }))
1319
- |> Dict.fromList
1320
-
1321
- part2 : Dict String Form.FieldState
1322
- part2 =
1323
- formState.pageFormState
1324
- |> Dict.get formId
1325
- |> Maybe.withDefault
1326
- (maybe
1327
- |> Maybe.map
1328
- (\{ fields } ->
1329
- { fields =
1330
- fields
1331
- |> List.map (Tuple.mapSecond (\value -> { value = value, status = FieldStatus.NotVisited }))
1332
- |> Dict.fromList
1333
- , submitAttempted = True
1334
- }
1335
- )
1336
- |> Maybe.withDefault initFormState
1337
- )
1338
- |> .fields
1339
-
1340
- fullFormState : Dict String Form.FieldState
1341
- fullFormState =
1342
- initialValues
1343
- |> Dict.union part2
1344
-
1345
- parsed :
1346
- { result : ( Validation error parsed named constraints, Dict String (List error) )
1347
- , isMatchCandidate : Bool
1348
- , view : Context error data -> List view
1349
- }
1350
- parsed =
1351
- parser (Just data) thisFormState
1352
-
1353
- withoutServerErrors : Validation error parsed named constraints
1354
- withoutServerErrors =
1355
- parsed |> mergeResults
1356
-
1357
- withServerErrors : Validation error parsed named constraints
1358
- withServerErrors =
1359
- mergeResults
1360
- { parsed
1361
- | result =
1362
- parsed.result
1363
- |> Tuple.mapSecond
1364
- (\errors1 ->
1365
- mergeErrors errors1
1366
- (maybe
1367
- |> Maybe.map .errors
1368
- |> Maybe.withDefault Dict.empty
1369
- )
1370
- )
1371
- }
1372
-
1373
- thisFormState : FormState
1374
- thisFormState =
1375
- formState.pageFormState
1376
- |> Dict.get formId
1377
- |> Maybe.withDefault
1378
- (maybe
1379
- |> Maybe.map
1380
- (\{ fields } ->
1381
- { fields =
1382
- fields
1383
- |> List.map (Tuple.mapSecond (\value -> { value = value, status = FieldStatus.NotVisited }))
1384
- |> Dict.fromList
1385
- , submitAttempted = True
1386
- }
1387
- )
1388
- |> Maybe.withDefault Form.init
1389
- )
1390
- |> (\state -> { state | fields = fullFormState })
1391
-
1392
- context : Context error data
1393
- context =
1394
- { errors =
1395
- withServerErrors
1396
- |> unwrapValidation
1397
- |> Tuple.second
1398
- |> Errors
1399
- , isTransitioning =
1400
- -- TODO instead of isTransitioning : Bool, it would be useful to get a custom type with the exact state
1401
- (formState.fetchers |> Dict.member formId)
1402
- || (case formState.transition of
1403
- Just (Submitting formData) ->
1404
- formData.id == Just formId
1405
-
1406
- Just (LoadAfterSubmit submitData _ _) ->
1407
- submitData.id == Just formId
1408
-
1409
- Just (Loading _ _) ->
1410
- False
1411
-
1412
- Nothing ->
1413
- False
1414
- )
1415
- , submitAttempted = thisFormState.submitAttempted
1416
- , data = data
1417
- }
1418
-
1419
- children : List view
1420
- children =
1421
- parsed.view context
1422
-
1423
- hiddenInputs : List view
1424
- hiddenInputs =
1425
- fieldDefinitions
1426
- |> List.filterMap
1427
- (\( name, fieldDefinition ) ->
1428
- case fieldDefinition of
1429
- HiddenField ->
1430
- [ Attr.name name
1431
- , Attr.type_ "hidden"
1432
- , Attr.value
1433
- (initialValues
1434
- |> Dict.get name
1435
- |> Maybe.map .value
1436
- |> Maybe.withDefault ""
1437
- )
1438
- ]
1439
- |> toHiddenInput
1440
- |> Just
1441
-
1442
- RegularField ->
1443
- Nothing
1444
- )
1445
-
1446
- isValid : Bool
1447
- isValid =
1448
- case withoutServerErrors of
1449
- Validation _ _ ( Just _, errors ) ->
1450
- Dict.isEmpty errors
1451
-
1452
- _ ->
1453
- False
1454
- in
1455
- { formId = formId
1456
- , hiddenInputs = hiddenInputs
1457
- , children = children
1458
- , isValid = isValid
1459
- }
1460
-
1461
-
1462
- {-| -}
1463
- type alias DoneForm error parsed data view =
1464
- Form
1465
- error
1466
- { combine : Combined error parsed
1467
- , view : Context error data -> view
1468
- }
1469
- data
1470
-
1471
-
1472
- {-| -}
1473
- type alias HtmlForm error parsed input msg =
1474
- Form
1475
- error
1476
- { combine : Combined error parsed
1477
- , view : Context error input -> List (Html (Pages.Msg.Msg msg))
1478
- }
1479
- input
1480
-
1481
-
1482
- {-| -}
1483
- type ServerForms error parsed
1484
- = ServerForms
1485
- (List
1486
- (Form
1487
- error
1488
- (Combined error parsed)
1489
- Never
1490
- )
1491
- )
1492
-
1493
-
1494
- {-| -}
1495
- initCombined :
1496
- (parsed -> combined)
1497
- ->
1498
- Form
1499
- error
1500
- { combineAndView
1501
- | combine : Validation error parsed kind constraints
1502
- }
1503
- input
1504
- -> ServerForms error combined
1505
- initCombined mapFn (Form _ parseFn _) =
1506
- ServerForms
1507
- [ Form
1508
- []
1509
- (\_ formState ->
1510
- let
1511
- foo :
1512
- { result : Dict String (List error)
1513
- , isMatchCandidate : Bool
1514
- , combineAndView : { combineAndView | combine : Validation error parsed kind constraints }
1515
- }
1516
- foo =
1517
- parseFn Nothing formState
1518
- in
1519
- { result = foo.result
1520
- , combineAndView = foo.combineAndView.combine |> Validation.mapWithNever mapFn
1521
- , isMatchCandidate = foo.isMatchCandidate
1522
- }
1523
- )
1524
- (\_ -> [])
1525
- ]
1526
-
1527
-
1528
- {-| -}
1529
- combine :
1530
- (parsed -> combined)
1531
- ->
1532
- Form
1533
- error
1534
- { combineAndView
1535
- | combine : Validation error parsed kind constraints
1536
- }
1537
- input
1538
- -> ServerForms error combined
1539
- -> ServerForms error combined
1540
- combine mapFn (Form _ parseFn _) (ServerForms serverForms) =
1541
- ServerForms
1542
- (serverForms
1543
- ++ [ Form []
1544
- (\_ formState ->
1545
- let
1546
- foo :
1547
- { result : Dict String (List error)
1548
- , isMatchCandidate : Bool
1549
- , combineAndView : { combineAndView | combine : Validation error parsed kind constraints }
1550
- }
1551
- foo =
1552
- parseFn Nothing formState
1553
- in
1554
- { result = foo.result
1555
- , combineAndView = foo.combineAndView.combine |> Validation.mapWithNever mapFn
1556
- , isMatchCandidate = foo.isMatchCandidate
1557
- }
1558
- )
1559
- (\_ -> [])
1560
- ]
1561
- )
1562
-
1563
-
1564
- {-| -}
1565
- initCombinedServer :
1566
- (parsed -> combined)
1567
- ->
1568
- Form
1569
- error
1570
- { combineAndView
1571
- | combine : Combined error (DataSource (Validation error parsed kind constraints))
1572
- }
1573
- input
1574
- -> ServerForms error (DataSource (Validation error combined kind constraints))
1575
- initCombinedServer mapFn serverForms =
1576
- initCombined (DataSource.map (Validation.map mapFn)) serverForms
1577
-
1578
-
1579
- {-| -}
1580
- combineServer :
1581
- (parsed -> combined)
1582
- ->
1583
- Form
1584
- error
1585
- { combineAndView
1586
- | combine :
1587
- Combined error (DataSource (Validation error parsed kind constraints))
1588
- }
1589
- input
1590
- -> ServerForms error (DataSource (Validation error combined kind constraints))
1591
- -> ServerForms error (DataSource (Validation error combined kind constraints))
1592
- combineServer mapFn a b =
1593
- combine (DataSource.map (Validation.map mapFn)) a b
1594
-
1595
-
1596
- {-| -}
1597
- type alias StyledHtmlForm error parsed data msg =
1598
- Form
1599
- error
1600
- { combine : Combined error parsed
1601
- , view : Context error data -> List (Html.Styled.Html (Pages.Msg.Msg msg))
1602
- }
1603
- data
1604
-
1605
-
1606
- {-| -}
1607
- type FormInternal error parsed data view
1608
- = FormInternal
1609
- -- TODO for renderCustom, pass them as an argument with all hidden fields that the user must render
1610
- (List ( String, FieldDefinition ))
1611
- (Maybe data
1612
- -> FormState
1613
- ->
1614
- { result :
1615
- ( parsed
1616
- , Dict String (List error)
1617
- )
1618
- , isMatchCandidate : Bool
1619
- , view : view
1620
- }
1621
- )
1622
- (data -> List ( String, String ))
1623
-
1624
-
1625
- {-| -}
1626
- type Form error combineAndView input
1627
- = Form
1628
- (List ( String, FieldDefinition ))
1629
- (Maybe input
1630
- -> FormState
1631
- ->
1632
- { result : Dict String (List error)
1633
- , isMatchCandidate : Bool
1634
- , combineAndView : combineAndView
1635
- }
1636
- )
1637
- (input -> List ( String, String ))
1638
-
1639
-
1640
- type alias RenderOptions userMsg =
1641
- { submitStrategy : SubmitStrategy
1642
- , method : Method
1643
- , name : Maybe String
1644
- , onSubmit : Maybe ({ fields : List ( String, String ) } -> userMsg)
1645
- }
1646
-
1647
-
1648
- {-| -}
1649
- type Method
1650
- = Post
1651
- | Get
1652
-
1653
-
1654
- methodToString : Method -> String
1655
- methodToString method =
1656
- case method of
1657
- Post ->
1658
- "POST"
1659
-
1660
- Get ->
1661
- "GET"
1662
-
1663
-
1664
- {-| -}
1665
- type SubmitStrategy
1666
- = FetcherStrategy
1667
- | TransitionStrategy
1668
-
1669
-
1670
- {-| -}
1671
- type FieldDefinition
1672
- = RegularField
1673
- | HiddenField
1674
-
1675
-
1676
- {-| -}
1677
- addErrorsInternal : String -> List error -> Dict String (List error) -> Dict String (List error)
1678
- addErrorsInternal name newErrors allErrors =
1679
- allErrors
1680
- |> Dict.update name
1681
- (\errors ->
1682
- Just (newErrors ++ (errors |> Maybe.withDefault []))
1683
- )