elm-pages 3.0.0-beta.4 → 3.0.0-beta.41
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.
- package/README.md +10 -1
- package/adapter/netlify.js +207 -0
- package/codegen/{elm-pages-codegen.js → elm-pages-codegen.cjs} +2828 -2933
- package/generator/dead-code-review/elm-stuff/tests-0.19.1/elm-stuff/0.19.1/Pages-Review-DeadCodeEliminateData.elmi +0 -0
- package/generator/dead-code-review/elm-stuff/tests-0.19.1/elm-stuff/0.19.1/Pages-Review-DeadCodeEliminateData.elmo +0 -0
- package/generator/dead-code-review/elm-stuff/tests-0.19.1/elm-stuff/0.19.1/Pages-Review-DeadCodeEliminateDataTest.elmo +0 -0
- package/generator/dead-code-review/elm-stuff/tests-0.19.1/elm-stuff/0.19.1/d.dat +0 -0
- package/generator/dead-code-review/elm-stuff/tests-0.19.1/elm-stuff/0.19.1/i.dat +0 -0
- package/generator/dead-code-review/elm-stuff/tests-0.19.1/elm-stuff/0.19.1/o.dat +0 -0
- package/generator/dead-code-review/elm-stuff/tests-0.19.1/elm.json +1 -1
- package/generator/dead-code-review/elm-stuff/tests-0.19.1/js/Reporter.elm.js +1447 -342
- package/generator/dead-code-review/elm-stuff/tests-0.19.1/js/Runner.elm.js +17004 -13817
- package/generator/dead-code-review/elm-stuff/tests-0.19.1/js/node_runner.js +1 -1
- package/generator/dead-code-review/elm-stuff/tests-0.19.1/js/node_supervisor.js +4 -4
- package/generator/dead-code-review/elm.json +9 -7
- package/generator/dead-code-review/src/Pages/Review/DeadCodeEliminateData.elm +59 -10
- package/generator/dead-code-review/tests/Pages/Review/DeadCodeEliminateDataTest.elm +52 -36
- package/generator/review/elm-stuff/tests-0.19.1/elm-stuff/0.19.1/Pages-Internal-RoutePattern.elmi +0 -0
- package/generator/review/elm-stuff/tests-0.19.1/elm-stuff/0.19.1/Pages-Internal-RoutePattern.elmo +0 -0
- package/generator/review/elm-stuff/tests-0.19.1/elm-stuff/0.19.1/Pages-Review-NoContractViolations.elmi +0 -0
- package/generator/review/elm-stuff/tests-0.19.1/elm-stuff/0.19.1/Pages-Review-NoContractViolations.elmo +0 -0
- package/generator/review/elm-stuff/tests-0.19.1/elm-stuff/0.19.1/d.dat +0 -0
- package/generator/review/elm-stuff/tests-0.19.1/elm-stuff/0.19.1/i.dat +0 -0
- package/generator/review/elm-stuff/tests-0.19.1/elm-stuff/0.19.1/o.dat +0 -0
- package/generator/review/elm-stuff/tests-0.19.1/elm.json +1 -1
- package/generator/review/elm-stuff/tests-0.19.1/js/Reporter.elm.js +1447 -342
- package/generator/review/elm-stuff/tests-0.19.1/js/Runner.elm.js +25025 -21739
- package/generator/review/elm-stuff/tests-0.19.1/js/node_runner.js +1 -1
- package/generator/review/elm-stuff/tests-0.19.1/js/node_supervisor.js +4 -4
- package/generator/review/elm.json +10 -10
- package/generator/src/RouteBuilder.elm +121 -114
- package/generator/src/SharedTemplate.elm +8 -7
- package/generator/src/SiteConfig.elm +3 -2
- package/generator/src/basepath-middleware.js +3 -3
- package/generator/src/build.js +209 -92
- package/generator/src/cli.js +292 -88
- package/generator/src/codegen.js +29 -27
- package/generator/src/compatibility-key.js +3 -0
- package/generator/src/compile-elm.js +43 -26
- package/generator/src/config.js +39 -0
- package/generator/src/copy-dir.js +2 -2
- package/generator/src/dev-server.js +176 -138
- package/generator/src/dir-helpers.js +9 -26
- package/generator/src/elm-codegen.js +5 -4
- package/generator/src/elm-file-constants.js +2 -3
- package/generator/src/error-formatter.js +12 -11
- package/generator/src/file-helpers.js +3 -4
- package/generator/src/generate-template-module-connector.js +23 -23
- package/generator/src/init.js +9 -8
- package/generator/src/pre-render-html.js +39 -28
- package/generator/src/render-test.js +109 -0
- package/generator/src/render-worker.js +25 -28
- package/generator/src/render.js +321 -142
- package/generator/src/request-cache.js +265 -162
- package/generator/src/resolve-elm-module.js +64 -0
- package/generator/src/rewrite-client-elm-json.js +6 -5
- package/generator/src/rewrite-elm-json-help.js +56 -0
- package/generator/src/rewrite-elm-json.js +17 -7
- package/generator/src/route-codegen-helpers.js +16 -31
- package/generator/src/seo-renderer.js +12 -7
- package/generator/src/vite-utils.js +77 -0
- package/generator/static-code/elm-pages.js +10 -0
- package/generator/static-code/hmr.js +79 -13
- package/generator/template/app/Api.elm +6 -5
- package/generator/template/app/Effect.elm +123 -0
- package/generator/template/app/ErrorPage.elm +37 -6
- package/generator/template/app/Route/Index.elm +17 -10
- package/generator/template/app/Shared.elm +24 -47
- package/generator/template/app/Site.elm +19 -6
- package/generator/template/app/View.elm +1 -8
- package/generator/template/elm-tooling.json +0 -3
- package/generator/template/elm.json +32 -24
- package/generator/template/package.json +10 -4
- package/package.json +30 -27
- package/src/ApiRoute.elm +199 -61
- package/src/BackendTask/Custom.elm +325 -0
- package/src/BackendTask/Env.elm +90 -0
- package/src/{DataSource → BackendTask}/File.elm +171 -56
- package/src/{DataSource → BackendTask}/Glob.elm +136 -125
- package/src/BackendTask/Http.elm +679 -0
- package/src/{DataSource → BackendTask}/Internal/Glob.elm +1 -1
- package/src/BackendTask/Internal/Request.elm +69 -0
- package/src/BackendTask/Random.elm +79 -0
- package/src/BackendTask/Time.elm +47 -0
- package/src/BackendTask.elm +531 -0
- package/src/FatalError.elm +90 -0
- package/src/Head/Seo.elm +4 -4
- package/src/Head.elm +237 -7
- package/src/HtmlPrinter.elm +7 -3
- package/src/Internal/ApiRoute.elm +7 -5
- package/src/PageServerResponse.elm +6 -1
- package/src/Pages/ConcurrentSubmission.elm +127 -0
- package/src/Pages/Form.elm +340 -0
- package/src/Pages/FormData.elm +18 -0
- package/src/Pages/GeneratorProgramConfig.elm +15 -0
- package/src/Pages/Internal/FatalError.elm +5 -0
- package/src/Pages/Internal/Msg.elm +93 -0
- package/src/Pages/Internal/NotFoundReason.elm +4 -4
- package/src/Pages/Internal/Platform/Cli.elm +617 -768
- package/src/Pages/Internal/Platform/CompatibilityKey.elm +6 -0
- package/src/Pages/Internal/Platform/Effect.elm +1 -2
- package/src/Pages/Internal/Platform/GeneratorApplication.elm +379 -0
- package/src/Pages/Internal/Platform/StaticResponses.elm +65 -276
- package/src/Pages/Internal/Platform/ToJsPayload.elm +6 -9
- package/src/Pages/Internal/Platform.elm +359 -225
- package/src/Pages/Internal/ResponseSketch.elm +2 -2
- package/src/Pages/Internal/Script.elm +17 -0
- package/src/Pages/Internal/StaticHttpBody.elm +35 -1
- package/src/Pages/Manifest.elm +52 -11
- package/src/Pages/Navigation.elm +87 -0
- package/src/Pages/PageUrl.elm +26 -12
- package/src/Pages/ProgramConfig.elm +35 -23
- package/src/Pages/Script.elm +166 -0
- package/src/Pages/SiteConfig.elm +3 -2
- package/src/Pages/StaticHttp/Request.elm +2 -2
- package/src/Pages/StaticHttpRequest.elm +23 -99
- package/src/Pages/Url.elm +3 -3
- package/src/PagesMsg.elm +88 -0
- package/src/QueryParams.elm +21 -172
- package/src/RenderRequest.elm +7 -7
- package/src/RequestsAndPending.elm +37 -20
- package/src/Result/Extra.elm +26 -0
- package/src/Scaffold/Form.elm +569 -0
- package/src/Scaffold/Route.elm +1411 -0
- package/src/Server/Request.elm +74 -72
- package/src/Server/Session.elm +62 -42
- package/src/Server/SetCookie.elm +80 -32
- package/src/Stub.elm +53 -0
- package/src/Test/Html/Internal/ElmHtml/ToString.elm +8 -9
- package/src/{Path.elm → UrlPath.elm} +33 -36
- package/src/DataSource/Env.elm +0 -38
- package/src/DataSource/Http.elm +0 -446
- package/src/DataSource/Internal/Request.elm +0 -20
- package/src/DataSource/Port.elm +0 -90
- package/src/DataSource.elm +0 -538
- package/src/Form/Field.elm +0 -717
- package/src/Form/FieldStatus.elm +0 -36
- package/src/Form/FieldView.elm +0 -417
- package/src/Form/FormData.elm +0 -22
- package/src/Form/Validation.elm +0 -391
- package/src/Form/Value.elm +0 -118
- package/src/Form.elm +0 -1683
- package/src/FormDecoder.elm +0 -102
- package/src/Pages/FormState.elm +0 -256
- package/src/Pages/Generate.elm +0 -800
- package/src/Pages/Internal/Form.elm +0 -17
- package/src/Pages/Msg.elm +0 -79
- package/src/Pages/Transition.elm +0 -70
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
|
-
)
|