refactor Form module
This commit is contained in:
8
frontend/app/Form.hs
Normal file
8
frontend/app/Form.hs
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
module Form
|
||||||
|
( module Form.Internal,
|
||||||
|
module Form.Input,
|
||||||
|
)
|
||||||
|
where
|
||||||
|
|
||||||
|
import Form.Input
|
||||||
|
import Form.Internal
|
||||||
26
frontend/app/Form/Input.hs
Normal file
26
frontend/app/Form/Input.hs
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
module Form.Input
|
||||||
|
( string,
|
||||||
|
)
|
||||||
|
where
|
||||||
|
|
||||||
|
import Data.Text qualified as T
|
||||||
|
import Form.Internal
|
||||||
|
import Miso
|
||||||
|
import Miso.String (toMisoString)
|
||||||
|
|
||||||
|
string :: String -> Form T.Text T.Text
|
||||||
|
string label =
|
||||||
|
Form
|
||||||
|
{ view = \i ->
|
||||||
|
[ div_ [] $
|
||||||
|
[ label_ [] $
|
||||||
|
[ text (toMisoString label),
|
||||||
|
input_
|
||||||
|
[ type_ "text",
|
||||||
|
value_ (toMisoString i)
|
||||||
|
]
|
||||||
|
]
|
||||||
|
]
|
||||||
|
],
|
||||||
|
fill = \i -> Right i
|
||||||
|
}
|
||||||
60
frontend/app/Form/Internal.hs
Normal file
60
frontend/app/Form/Internal.hs
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
module Form.Internal
|
||||||
|
( Form (..),
|
||||||
|
mapValues,
|
||||||
|
runForm,
|
||||||
|
)
|
||||||
|
where
|
||||||
|
|
||||||
|
import Miso
|
||||||
|
|
||||||
|
data Form i o = Form
|
||||||
|
{ view :: i -> [View i],
|
||||||
|
fill :: i -> Either String o
|
||||||
|
}
|
||||||
|
|
||||||
|
instance Functor (Form i) where
|
||||||
|
fmap f (Form {view, fill}) =
|
||||||
|
Form
|
||||||
|
{ fill = fmap f . fill,
|
||||||
|
..
|
||||||
|
}
|
||||||
|
|
||||||
|
instance Applicative (Form i) where
|
||||||
|
pure x =
|
||||||
|
Form
|
||||||
|
{ view = const [],
|
||||||
|
fill = const (Right x)
|
||||||
|
}
|
||||||
|
|
||||||
|
f <*> x =
|
||||||
|
Form
|
||||||
|
{ view = liftA2 (<>) f.view x.view,
|
||||||
|
fill = \i -> ($) <$> f.fill i <*> x.fill i
|
||||||
|
}
|
||||||
|
|
||||||
|
instance Monad (Form i) where
|
||||||
|
form >>= mkForm =
|
||||||
|
Form
|
||||||
|
{ view = \i ->
|
||||||
|
form.view i
|
||||||
|
<> case form.fill i of
|
||||||
|
Right x -> (mkForm x).view i
|
||||||
|
Left _ -> [],
|
||||||
|
fill = \i -> case form.fill i of
|
||||||
|
Right x -> (mkForm x).fill i
|
||||||
|
Left e -> Left e
|
||||||
|
}
|
||||||
|
|
||||||
|
mapValues :: (i' -> i) -> (i -> i' -> i') -> Form i o -> Form i' o
|
||||||
|
mapValues get set (Form {view, fill}) =
|
||||||
|
Form
|
||||||
|
{ view = \i -> fmap (flip set i) <$> view (get i),
|
||||||
|
fill = fill . get
|
||||||
|
}
|
||||||
|
|
||||||
|
runForm :: Form i o -> i -> View (Either i o)
|
||||||
|
runForm (Form {view}) i =
|
||||||
|
div_ [] $
|
||||||
|
(fmap Left <$> view i)
|
||||||
|
<> [ button_ [type_ "submit"] [text "submit"]
|
||||||
|
]
|
||||||
@@ -22,6 +22,7 @@ import Data.Function
|
|||||||
import Data.List
|
import Data.List
|
||||||
import Data.Map qualified as M
|
import Data.Map qualified as M
|
||||||
import Data.Text qualified as T
|
import Data.Text qualified as T
|
||||||
|
import Form qualified as F
|
||||||
import GHC.Generics (Generic)
|
import GHC.Generics (Generic)
|
||||||
import Miso
|
import Miso
|
||||||
import Miso.String (toMisoString)
|
import Miso.String (toMisoString)
|
||||||
@@ -164,7 +165,7 @@ viewModel model =
|
|||||||
( either
|
( either
|
||||||
err
|
err
|
||||||
( fmap (either FormChanged FormSubmitted)
|
( fmap (either FormChanged FormSubmitted)
|
||||||
. flip viewForm (A.Object AM.empty)
|
. flip F.runForm (A.Object AM.empty)
|
||||||
. schemaForm
|
. schemaForm
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
@@ -188,15 +189,16 @@ viewSchema schema =
|
|||||||
)
|
)
|
||||||
<$> (M.toList properties)
|
<$> (M.toList properties)
|
||||||
|
|
||||||
schemaForm :: Schema -> Form A.Value A.Value
|
schemaForm :: Schema -> F.Form A.Value A.Value
|
||||||
schemaForm schema =
|
schemaForm schema =
|
||||||
mapOutput mergeJson . sequence $
|
fmap mergeJson . sequence $
|
||||||
case schema.type_ of
|
case schema.type_ of
|
||||||
Object properties ->
|
Object properties ->
|
||||||
( \(AK.fromString -> k, "string") ->
|
( \(AK.fromString -> k, "string") ->
|
||||||
mapOutput (A.Object . AM.singleton k) $
|
A.Object . AM.singleton k
|
||||||
mapValues (getO k) (setO k) $
|
<$> ( F.mapValues (getO k) (setO k) $
|
||||||
jsonString (AK.toString k)
|
jsonString (AK.toString k)
|
||||||
|
)
|
||||||
)
|
)
|
||||||
<$> (M.toList properties)
|
<$> (M.toList properties)
|
||||||
|
|
||||||
@@ -219,85 +221,8 @@ getO k (A.Object kvs) = fromMaybe A.Null (AM.lookup k kvs)
|
|||||||
setO :: AK.Key -> A.Value -> A.Value -> A.Value
|
setO :: AK.Key -> A.Value -> A.Value -> A.Value
|
||||||
setO k v (A.Object kvs) = A.Object (AM.insert k v kvs)
|
setO k v (A.Object kvs) = A.Object (AM.insert k v kvs)
|
||||||
|
|
||||||
data Form i o = Form
|
jsonString :: String -> F.Form A.Value A.Value
|
||||||
{ view :: i -> [View i],
|
jsonString = fmap A.String . F.mapValues fromJson toJson . F.string
|
||||||
fill :: i -> Either String o
|
|
||||||
}
|
|
||||||
|
|
||||||
instance Functor (Form i) where
|
|
||||||
fmap f (Form {view, fill}) =
|
|
||||||
Form
|
|
||||||
{ fill = fmap f . fill,
|
|
||||||
..
|
|
||||||
}
|
|
||||||
|
|
||||||
instance Applicative (Form i) where
|
|
||||||
pure x =
|
|
||||||
Form
|
|
||||||
{ view = const [],
|
|
||||||
fill = const (Right x)
|
|
||||||
}
|
|
||||||
Form {view = viewF, fill = fillF} <*> Form {view = viewX, fill = fillX} =
|
|
||||||
Form
|
|
||||||
{ view = \i ->
|
|
||||||
let f = viewF i
|
|
||||||
x = viewX i
|
|
||||||
in f <> x,
|
|
||||||
fill = \i ->
|
|
||||||
let f = fillF i
|
|
||||||
x = fillX i
|
|
||||||
in ($) <$> f <*> x
|
|
||||||
}
|
|
||||||
|
|
||||||
instance Monad (Form i) where
|
|
||||||
(Form {view = viewM, fill = fillM}) >>= mkF =
|
|
||||||
Form
|
|
||||||
{ view = \i ->
|
|
||||||
viewM i
|
|
||||||
<> case fillM i of
|
|
||||||
Right x -> (mkF x).view i
|
|
||||||
Left _ -> [],
|
|
||||||
fill = \i -> case fillM i of
|
|
||||||
Right x -> (mkF x).fill i
|
|
||||||
Left e -> Left e
|
|
||||||
}
|
|
||||||
|
|
||||||
mapValues :: (i' -> i) -> (i -> i' -> i') -> Form i o -> Form i' o
|
|
||||||
mapValues get set (Form {view, fill}) =
|
|
||||||
Form
|
|
||||||
{ view = \i -> fmap (flip set i) <$> view (get i),
|
|
||||||
fill = fill . get
|
|
||||||
}
|
|
||||||
|
|
||||||
mapOutput :: (o -> o') -> Form i o -> Form i o'
|
|
||||||
mapOutput = fmap
|
|
||||||
|
|
||||||
viewForm :: Form i o -> i -> View (Either i o)
|
|
||||||
viewForm (Form {view}) i =
|
|
||||||
div_ [] $
|
|
||||||
(fmap Left <$> view i)
|
|
||||||
<> [ button_ [type_ "submit"] [text "submit"]
|
|
||||||
]
|
|
||||||
|
|
||||||
jsonString :: String -> Form A.Value A.Value
|
|
||||||
jsonString = mapOutput A.String . mapValues fromJson toJson . string
|
|
||||||
|
|
||||||
string :: String -> Form T.Text T.Text
|
|
||||||
string label =
|
|
||||||
Form
|
|
||||||
{ view = \i ->
|
|
||||||
[ div_ [] $
|
|
||||||
[ label_ [] $
|
|
||||||
[ text (toMisoString label),
|
|
||||||
input_
|
|
||||||
[ type_ "text",
|
|
||||||
value_ (toMisoString i)
|
|
||||||
]
|
|
||||||
]
|
|
||||||
]
|
|
||||||
],
|
|
||||||
fill = \i -> Right i
|
|
||||||
}
|
|
||||||
|
|
||||||
viewPosts :: [A.Value] -> View Action
|
viewPosts :: [A.Value] -> View Action
|
||||||
viewPosts posts = ol_ [] (viewPost <$> posts)
|
viewPosts posts = ol_ [] (viewPost <$> posts)
|
||||||
|
|||||||
@@ -11,6 +11,11 @@ extra-doc-files: CHANGELOG.md
|
|||||||
executable frontend
|
executable frontend
|
||||||
main-is: Main.hs
|
main-is: Main.hs
|
||||||
hs-source-dirs: app
|
hs-source-dirs: app
|
||||||
|
other-modules:
|
||||||
|
Form
|
||||||
|
Form.Input
|
||||||
|
Form.Internal
|
||||||
|
|
||||||
default-language: GHC2021
|
default-language: GHC2021
|
||||||
default-extensions:
|
default-extensions:
|
||||||
CPP OverloadedStrings RecordWildCards DeriveAnyClass
|
CPP OverloadedStrings RecordWildCards DeriveAnyClass
|
||||||
|
|||||||
Reference in New Issue
Block a user