{-# OPTIONS_GHC -fno-warn-deprecations #-} module Utils.Form where import ClassyPrelude.Yesod hiding (addMessage, cons, Proxy(..), identifyForm) import Settings import Utils.Parameters -- import Text.Blaze (toMarkup) -- for debugging import qualified Text.Blaze.Internal as Blaze (null) import qualified Data.Text as T import Data.CaseInsensitive (CI) import qualified Data.CaseInsensitive as CI import Data.Universe import Data.Map.Lazy ((!)) import qualified Data.Map.Lazy as Map import qualified Data.Set as Set import Control.Monad.Trans.Maybe (MaybeT(..)) import Control.Monad.Reader.Class (MonadReader(..)) import Control.Monad.Writer.Class (MonadWriter(..)) import Data.List ((!!)) import Control.Lens import Web.PathPieces import Data.UUID import Utils.Message import Utils.PathPiece import Data.Proxy ------------------- -- Form Renderer -- ------------------- -- | Use this type to pass information to the form template data FormLayout = FormStandard | FormDBTableFilter | FormDBTablePagesize renderAForm :: Monad m => FormLayout -> FormRender m a renderAForm formLayout aform fragment = do (res, ($ []) -> fieldViews) <- aFormToForm aform let widget = $(widgetFile "widgets/form/form") return (res, widget) -- | special id to identify form section headers, see 'aformSection' and 'formSection' -- currently only treated by form generation through 'renderAForm' idFormSectionNoinput :: Text idFormSectionNoinput = "form-section-noinput" -- | Generates a form having just a form-section-header and no input title. -- Currently only correctly rendered by 'renderAForm' and mforms using 'widget/form.hamlet' -- Usage: -- @ -- (,) <$ formSection MsgInt -- <*> areq intField "int here" Nothing -- <* formSection MsgDouble -- <*> areq doubleField "double there " Nothing -- <* submitButton -- @ -- If tooltips or other attributes are required, see 'formSection\'' instead. aformSection :: (MonadHandler m, site ~ HandlerSite m, RenderMessage site msg) => msg -> AForm m () aformSection = formToAForm . fmap (second pure) . formSection formSection :: (MonadHandler m, site ~ HandlerSite m, RenderMessage site msg) => msg -> MForm m (FormResult (), FieldView site) -- TODO: WIP, delete formSection formSectionTitle = do mr <- getMessageRender return (FormSuccess (), FieldView { fvLabel = toHtml $ mr formSectionTitle , fvTooltip = Nothing , fvId = idFormSectionNoinput , fvErrors = Nothing , fvRequired = False , fvInput = mempty }) -------------------- -- Field Settings -- -------------------- fsl :: Text -> FieldSettings site fsl lbl = FieldSettings { fsLabel = SomeMessage lbl , fsTooltip = Nothing , fsId = Nothing , fsName = Nothing , fsAttrs = [] } fslI :: RenderMessage site msg => msg -> FieldSettings site fslI lbl = FieldSettings { fsLabel = SomeMessage lbl , fsTooltip = Nothing , fsId = Nothing , fsName = Nothing , fsAttrs = [] } fslp :: Text -> Text -> FieldSettings site fslp lbl placeholder = FieldSettings { fsLabel = SomeMessage lbl , fsTooltip = Nothing , fsId = Nothing , fsName = Nothing , fsAttrs = [("placeholder", placeholder)] } fslpI :: RenderMessage site msg => msg -> Text -> FieldSettings site fslpI lbl placeholder = FieldSettings { fsLabel = SomeMessage lbl , fsTooltip = Nothing , fsId = Nothing , fsName = Nothing , fsAttrs = [("placeholder", placeholder)] } addAttr :: Text -> Text -> FieldSettings site -> FieldSettings site addAttr attr valu fs = fs { fsAttrs = newAttrs $ fsAttrs fs } where newAttrs :: [(Text,Text)] -> [(Text,Text)] newAttrs [] = [(attr, valu)] newAttrs (p@(a,v) : t) | attr==a = (a, T.append valu $ cons ' ' v) : t | otherwise = p : newAttrs t addAttrs :: Text -> [Text] -> FieldSettings site -> FieldSettings site addAttrs attr valus fs = fs { fsAttrs = newAttrs $ fsAttrs fs } where newAttrs :: [(Text, Text)] -> [(Text, Text)] newAttrs [] = [(attr, T.intercalate " " valus)] newAttrs (p@(a,v) : t) | attr==a = ( a, T.intercalate " " $ v : valus ) : t | otherwise = p : newAttrs t addClass :: Text -> FieldSettings site -> FieldSettings site addClass = addAttr "class" addClasses :: [Text] -> FieldSettings site -> FieldSettings site addClasses = addAttrs "class" addName :: Text -> FieldSettings site -> FieldSettings site addName nm fs = fs { fsName = Just nm } addNameClass :: Text -> Text -> FieldSettings site -> FieldSettings site addNameClass gName gClass fs = fs { fsName = Just gName, fsAttrs = ("class",gClass) : fsAttrs fs } addIdClass :: Text -> Text -> FieldSettings site -> FieldSettings site addIdClass gId gClass fs = fs { fsId = Just gId, fsAttrs = ("class",gClass) : fsAttrs fs } setClass :: FieldSettings site -> Text -> FieldSettings site -- deprecated setClass fs c = fs { fsAttrs = ("class",c) : fsAttrs fs } setNameClass :: FieldSettings site -> Text -> Text -> FieldSettings site -- deprecated setNameClass fs gName gClass = fs { fsName = Just gName , fsAttrs = ("class",gClass) : fsAttrs fs } setTooltip :: RenderMessage site msg => msg -> FieldSettings site -> FieldSettings site setTooltip msg fs = fs { fsTooltip = Just $ SomeMessage msg } addDatalist :: (PathPiece (Element vals), MonoFoldable vals, Monad m) => WidgetT (HandlerSite m) IO vals -> Field m a -> Field m a addDatalist mValues field = field { fieldView = \fId fName fAttrs fRes fReq -> do listId <- newIdent values <- map toPathPiece . otoList <$> mValues fieldView field fId fName (("list", listId) : fAttrs) fRes fReq [whamlet| $newline never $forall value <- values