{-# LANGUAGE NamedFieldPuns , ScopedTypeVariables , LambdaCase , TypeFamilies #-} -- | This module provides helper functions for HTML input elements. These helper functions are not specific to any particular web framework or html library. module Ditto.Generalized.Internal where import Control.Monad.State.Class (get) import Control.Monad.Trans (lift) import Data.Either import Data.List (find) import Data.List.NonEmpty (NonEmpty(..)) import Data.Traversable (for) import Ditto.Backend import Ditto.Core import Ditto.Types -- | used for constructing elements like @\@, which pure a single input value. input :: forall m input err a view. (Environment m input, FormError input err) => FormState m FormId -> (input -> Either err a) -> (FormId -> a -> view) -> a -> Form m input err view a input formSId fromInput toView initialValue = Form (pure . fromInput) (pure initialValue) $ do i <- formSId v <- getFormInput' i case v of Default -> pure ( View $ const $ toView i initialValue , Ok $ Proved { pos = unitRange i , unProved = initialValue } ) Found inp -> case fromInput inp of Right a -> pure ( View $ const $ toView i a , Ok $ Proved { pos = unitRange i , unProved = a } ) Left err -> pure ( View $ const $ toView i initialValue , Error [(unitRange i, err)] ) Missing -> pure ( View $ const $ toView i initialValue , Error [(unitRange i, commonFormError (InputMissing i :: CommonFormError input) :: err)] ) -- | this is necessary in order to basically map over the decoding function inputList :: forall m input err a view view'. (Monad m, FormError input err, Environment m input) => FormState m FormId -> (input -> m (Either err [a])) -- ^ decoding function for the list -> ([view] -> view') -- ^ how to concatenate views -> [a] -- ^ initial values -> view' -- ^ view to generate in the fail case -> (a -> Form m input err view a) -> Form m input err view' [a] inputList formSId fromInput viewCat initialValue defView createForm = Form fromInput (pure initialValue) $ do i <- formSId v <- getFormInput' i case v of Default -> do views <- for initialValue $ \x -> do (View viewF, _) <- formFormlet $ createForm x pure $ viewF [] pure ( View $ const $ viewCat views , Ok $ Proved { pos = unitRange i , unProved = initialValue } ) Found inp -> lift (fromInput inp) >>= \case Right xs -> do views <- for xs $ \x -> do (View viewF, _) <- formFormlet $ createForm x pure $ viewF [] pure ( View $ const $ viewCat views , Ok $ Proved { pos = unitRange i , unProved = xs } ) Left err -> do let err' = [(unitRange i, err)] views <- for initialValue $ \x -> do (View viewF, _) <- formFormlet $ createForm x pure $ viewF err' pure ( View $ const $ viewCat views , Error err' ) Missing -> do pure ( View $ const defView , Ok $ Proved { pos = unitRange i , unProved = [] } ) -- | used for elements like @\@ which are not always present in the form submission data. inputMaybe :: (Monad m, FormError input err, Environment m input) => FormState m FormId -> (input -> Either err a) -> (FormId -> Maybe a -> view) -> Maybe a -> Form m input err view (Maybe a) inputMaybe i' fromInput toView initialValue = Form (pure . fmap Just . fromInput) (pure initialValue) $ do i <- i' v <- getFormInput' i case v of Default -> pure ( View $ const $ toView i initialValue , Ok Proved { pos = unitRange i , unProved = initialValue } ) Found x -> case fromInput x of Right a -> pure ( View $ const $ toView i (Just a) , Ok Proved { pos = unitRange i , unProved = Just a } ) Left err -> pure ( View $ const $ toView i initialValue , Error [(unitRange i, err)] ) Missing -> pure ( View $ const $ toView i initialValue , Ok $ Proved { pos = unitRange i , unProved = Nothing } ) -- | used for elements like @\@ which take a value, but are never present in the form data set. inputNoData :: (Monad m) => FormState m FormId -> (FormId -> view) -> Form m input err view () inputNoData i' toView = Form (successDecode ()) (pure ()) $ do i <- i' pure ( View $ const $ toView i , Ok Proved { pos = unitRange i , unProved = () } ) -- | used for @\@ inputFile :: forall m ft input err view. (Monad m, FormInput input, FormError input err, Environment m input, ft ~ FileType input, Monoid ft) => FormState m FormId -> (FormId -> view) -> Form m input err view (FileType input) inputFile i' toView = Form (pure . getInputFile) (pure mempty) $ do -- FIXME i <- i' v <- getFormInput' i case v of Default -> pure ( View $ const $ toView i , Error [(unitRange i, commonFormError (InputMissing i :: CommonFormError input) :: err)] ) Found x -> case getInputFile x of Right a -> pure ( View $ const $ toView i , Ok Proved { pos = unitRange i , unProved = a } ) Left err -> pure ( View $ const $ toView i , Error [(unitRange i, err)] ) Missing -> pure ( View $ const $ toView i , Error [(unitRange i, commonFormError (InputMissing i :: CommonFormError input) ::err)] ) -- | used for groups of checkboxes, @\