This repository has been archived by the owner on Jun 15, 2023. It is now read-only.
/
State.purs
190 lines (160 loc) · 5.46 KB
/
State.purs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
module Text.Markdown.SlamDown.Halogen.Component.State
( FormFieldValue
, SlamDownFormDesc
, SlamDownFormState
, SlamDownStateR
, SlamDownState(..)
, emptySlamDownState
, getDocument
, getFormState
, modifyFormState
, syncState
, replaceDocument
, formDescFromDocument
, formStateFromDocument
, formFieldGetDefaultValue
, getFormFieldValue
) where
import Prelude
import Data.Const (Const(..))
import Data.Foldable as F
import Data.Identity as Id
import Data.List as L
import Data.Maybe as M
import Data.Monoid (mempty)
import Data.StrMap as SM
import Data.Tuple (Tuple(..))
import Data.Validation.Semigroup as V
import Test.StrongCheck.Gen as Gen
import Test.StrongCheck.Arbitrary as SCA
import Text.Markdown.SlamDown as SD
import Text.Markdown.SlamDown.Parser.Inline as SDPI
import Text.Markdown.SlamDown.Traverse as SDT
type FormFieldValue = SD.FormFieldP Id.Identity
type SlamDownFormDesc a = SM.StrMap (SD.FormField a)
type SlamDownFormState a = SM.StrMap (FormFieldValue a)
type SlamDownStateR a =
{ document ∷ SD.SlamDownP a
, formState ∷ SlamDownFormState a
}
-- | The state of a SlamDown form
newtype SlamDownState a = SlamDownState (SlamDownStateR a)
instance functorSlamDownState ∷ Functor SlamDownState where
map f (SlamDownState st) =
SlamDownState
{ document: f <$> st.document
, formState: map f <$> st.formState
}
getDocument ∷ SlamDownState ~> SD.SlamDownP
getDocument (SlamDownState rec) = rec.document
getFormState ∷ SlamDownState ~> SlamDownFormState
getFormState (SlamDownState rec) = rec.formState
modifyFormState
∷ ∀ a
. (SlamDownFormState a → SlamDownFormState a)
→ SlamDownState a
→ SlamDownState a
modifyFormState f (SlamDownState rec) =
SlamDownState (rec { formState = f rec.formState })
instance showSlamDownState ∷ (Show a) ⇒ Show (SlamDownState a) where
show (SlamDownState rec) = "(SlamDownState " <> show rec.formState <> ")"
instance arbitrarySlamDownState ∷ (SCA.Arbitrary a, Ord a) ⇒ SCA.Arbitrary (SlamDownState a) where
arbitrary = do
document ← SCA.arbitrary
formState ← SM.fromFoldable <$> SCA.arbitrary :: Gen.Gen (L.List (Tuple String (FormFieldValue a)))
pure $ SlamDownState
{ document : document
, formState : formState
}
-- | Gets the form field value, or the default if none is present.
getFormFieldValue
∷ ∀ v
. String
→ SlamDownState v
→ M.Maybe (FormFieldValue v)
getFormFieldValue key state =
case SM.lookup key $ getFormState state of
M.Just x → M.Just x
M.Nothing → SM.lookup key <<< formStateFromDocument $ getDocument state
formStateFromDocument ∷ SD.SlamDownP ~> SlamDownFormState
formStateFromDocument =
SM.fromFoldable
<<< SDT.everything (const mempty) phi
where
phi
∷ ∀ v
. SD.Inline v
→ L.List (Tuple String (FormFieldValue v))
phi (SD.FormField label _ field) =
M.maybe mempty (L.singleton <<< Tuple label) $
V.unV (const M.Nothing) M.Just (SDPI.validateFormField field)
>>= formFieldGetDefaultValue
phi _ = mempty
formFieldGetDefaultValue
∷ ∀ v
. SD.FormField v
→ M.Maybe (FormFieldValue v)
formFieldGetDefaultValue =
SD.traverseFormField (SD.getLiteral >>> map pure)
-- | The initial empty state of the form, with an empty document.
emptySlamDownState ∷ ∀ v. SlamDownState v
emptySlamDownState =
SlamDownState
{ document : SD.SlamDown mempty
, formState : SM.empty
}
-- | The initial state of the form based on a document value. All fields use
-- | their default values.
makeSlamDownState ∷ SD.SlamDownP ~> SlamDownState
makeSlamDownState doc =
SlamDownState
{ document : doc
, formState : formStateFromDocument doc
}
formDescFromDocument ∷ SD.SlamDownP ~> SlamDownFormDesc
formDescFromDocument =
SM.fromFoldable
<<< SDT.everything (const mempty) phi
where
phi ∷ ∀ v. SD.Inline v → L.List (Tuple String (SD.FormField v))
phi (SD.FormField label _ field) = L.singleton (Tuple label field)
phi _ = mempty
syncState
∷ ∀ v
. SD.Value v
⇒ SD.SlamDownP v
→ SlamDownFormState v
→ SlamDownState v
syncState doc formState =
SlamDownState
{ document: doc
, formState: formState'
}
where
formDesc ∷ SlamDownFormDesc v
formDesc = formDescFromDocument doc
eraseTextBox ∷ ∀ f. SD.TextBox f → SD.TextBox (Const Unit)
eraseTextBox = SD.transTextBox \_ → Const unit
-- | Returns the keys that are either not present in the new state, or have had their types changed.
keysToPrune ∷ SlamDownFormState v → Array String
keysToPrune =
SM.foldMap \key oldVal →
case SM.lookup key formDesc of
M.Nothing → [ key ]
M.Just formVal →
case oldVal, formVal of
SD.TextBox tb1, SD.TextBox tb2 | eraseTextBox tb1 == eraseTextBox tb2 → []
SD.CheckBoxes _ (Id.Identity xs1), SD.CheckBoxes _ (SD.Literal xs2) | xs1 == xs2 → []
SD.DropDown _ (Id.Identity xs1), SD.DropDown _ (SD.Literal xs2) | xs1 == xs2 → []
SD.RadioButtons _ (Id.Identity xs1), SD.RadioButtons _ (SD.Literal xs2) | xs1 == xs2 → []
_, _ → [ key ]
formState' ∷ SlamDownFormState v
formState' = F.foldr SM.delete formState $ keysToPrune formState
replaceDocument
∷ ∀ v
. (SD.Value v)
⇒ SD.SlamDownP v
→ SlamDownState v
→ SlamDownState v
replaceDocument doc (SlamDownState state) =
syncState doc state.formState